[p4est] 01/01: Imported Upstream version 1.1

Matthias Maier tamiko-guest at moszumanska.debian.org
Thu Jul 14 14:05:10 UTC 2016


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

tamiko-guest pushed a commit to annotated tag upstream/1.1
in repository p4est.

commit 7f7e073f59d56878885c938743b463855c8383ee
Author: Matthias Maier <tamiko+DEBIAN at kyomu.43-1.org>
Date:   Thu Jul 14 13:56:37 2016 +0000

    Imported Upstream version 1.1
---
 .gitmodules                                        |    4 +
 AUTHORS                                            |   72 +
 COPYING                                            |  339 ++
 Doxyfile.in                                        | 2310 +++++++++++
 INSTALL                                            |  291 ++
 Makefile.am                                        |  120 +
 Makefile.p4est.pre.in                              |   24 +
 NEWS                                               |    4 +
 README                                             |  172 +
 bootstrap                                          |   39 +
 ...e-186d1690026feedd7633c97839bf443b534e6f70.yaml |   25 +
 ...e-19d672a00739ace2908339f96aa340ce1097f02b.yaml |   40 +
 ...e-1bcc4bde086f347681a9c33d7e395d6718ae1883.yaml |   43 +
 ...e-1fa6eb95c70d012c57d06e3683c0ac6a393ccc7b.yaml |   24 +
 ...e-2b15a0df0596919470477c98141355b80cab53bc.yaml |   24 +
 ...e-2ee86494dff916b22697a711be761c6e4278b349.yaml |   24 +
 ...e-3209cbc7fe045c201f5f0b4af7951a3b81c164ab.yaml |   36 +
 ...e-327c7e8894b128ebd016656f8f4ba10e34f6f622.yaml |   24 +
 ...e-3bfc08f604a148d0535c01dd830f1eadc7933ecd.yaml |   18 +
 ...e-3c9b2ae49358d99afd662085c2bd2d5772309e56.yaml |   30 +
 ...e-3ca5f0c70f57849b33c416465abe25c00db8f3af.yaml |   18 +
 ...e-3cd73c7e60fb75ee11ee649343679880063eda77.yaml |   31 +
 ...e-40109afca06b4e94bbb61bb2949afca54a85da45.yaml |   22 +
 ...e-47e1146b570aa7160ad7bbff091a220a4e3f8800.yaml |   26 +
 ...e-4c0103ff75bb0fc5249a6552c6ac3634f8597a6a.yaml |   25 +
 ...e-5297112884f1f36a0a9dd9bec2de90481ccdf7b7.yaml |   28 +
 ...e-52b927ff7d7bab4ea8f05c9d153dddba25199564.yaml |   22 +
 ...e-6c6239e053919531627e706685ed4c811e474eda.yaml |   22 +
 ...e-6c92ff2071235b8fd8c279a135bba542f34d03f0.yaml |   39 +
 ...e-702339262c6678519fb1edc5de7d67e3af640eae.yaml |   24 +
 ...e-717986f9897b50adad9096285c6a86ae007729a1.yaml |   29 +
 ...e-78e0a8c03a44fca337602cf71cc983d2eef93290.yaml |   37 +
 ...e-79ecc3d40e82827970b29b493f68b3f1b6ec574c.yaml |   39 +
 ...e-7a606717f830cd9686ebaa301e13ee80dba3be1c.yaml |   22 +
 ...e-7ab3610df158776fa1572b84e130635772281b35.yaml |   31 +
 ...e-84a74da0a3a4f357666853d81ce8280d22d3d6e1.yaml |   31 +
 ...e-8bb5112f86923515c14a987340d571ade618963a.yaml |   28 +
 ...e-927e4b037e69d0b970faa9e882fde8519f9b21b7.yaml |   25 +
 ...e-92dbc3c718f4517055012c7206a9b2ac8171298b.yaml |   22 +
 ...e-a1c658fed16cb6905d1f2a36f2f6d31129cfd626.yaml |   33 +
 ...e-a456c21b12a649522ff76e7e34120e06609f3d36.yaml |   36 +
 ...e-a45b59075f1c1830366c414703090e6ed4875a45.yaml |   32 +
 ...e-a54552a06eb8c1c9130c969909ef566c84b217ef.yaml |   31 +
 ...e-acb1ebc87d79bd1744490f330b0e0abdbe7944aa.yaml |   22 +
 ...e-b295c3069e005f7679d9fca88f890bd1a54f2535.yaml |   22 +
 ...e-bd78e83b886f8721a5acd9bbb43e9c6a52d0c972.yaml |   32 +
 ...e-bd7f52770dfd69f68b648f6388fb538ff335ef4c.yaml |   24 +
 ...e-bf1b9e01cc043735905673a2e7c1a25e5ed7a58f.yaml |   22 +
 ...e-cad051b8c32fd80d9c39303096e34988b526d0b0.yaml |   24 +
 ...e-cfdac5bda3344be1e1cdb6a7f70ceac8a1257cf8.yaml |   43 +
 ...e-d0e99b9f6d2204e88c8e00a2c0812ae65c2910d5.yaml |   21 +
 ...e-d201dca19725faf673774d97bd0bf41ff6a73da4.yaml |   27 +
 ...e-d5068a39cb5f38a40f234e9552119fbf4a2a791d.yaml |   28 +
 ...e-d93672ccf1d9e0283d94c6952d2f7d3b0530de27.yaml |   30 +
 ...e-e05575fcbdce35574cac3d09b8688ec77c791db0.yaml |   25 +
 ...e-e12f2621a32bbffc2a9704a189e8229d3dbaf412.yaml |   24 +
 ...e-e3fb3ea10bbe0196c325c92744bfc3eab28c98da.yaml |   28 +
 ...e-e83652f1ee3e88a3ee7917ea288139cb7245f937.yaml |   36 +
 ...e-ea05cc814c6b8c0c5ffb97c518cedc9bf7d55d10.yaml |   26 +
 ...e-ee03a7e2b63af3a9124e968631c1c0a1fc67d09d.yaml |   20 +
 ...e-ee31c9c10ebd498fb6756ec3d2f0781602c86191.yaml |   24 +
 ...e-f92033d821dcf3de6c4e863ea163100ee0f7a069.yaml |   25 +
 bugs/project.yaml                                  |   38 +
 build-aux/git-version-gen                          |  162 +
 build-aux/git2cl                                   |  308 ++
 config/p4est_include.m4                            |   76 +
 config/p4est_metis.m4                              |   49 +
 config/p4est_petsc.m4                              |   75 +
 configure.ac                                       |  105 +
 doc/attic/balance.txt                              |   68 +
 doc/attic/balance_helpers.txt                      |   37 +
 doc/attic/checksums.txt                            |   20 +
 doc/attic/connectivity.txt                         |   24 +
 doc/attic/p8est_trilinear.c                        |   42 +
 doc/attic/p8est_trilinear.h                        |  344 ++
 doc/attic/p8est_trilinear_lnodes.c                 |  619 +++
 doc/attic/p8est_trilinear_nodes.c                  |  263 ++
 doc/attic/scalings.txt                             |  162 +
 doc/author_holke.txt                               |    1 +
 doc/coding_standards.txt                           |   54 +
 doc/connectivity.txt                               |   53 +
 doc/doxygen.css                                    | 1164 ++++++
 doc/juqueen/setup-p4est                            |   63 +
 doc/mainpage.dox                                   |   74 +
 doc/p4est-setup.sh                                 |  110 +
 doc/tex/interface-howto.tex                        |  317 ++
 doc/tex/octreemap.tex                              |   88 +
 doc/tex/tikzstyles.tex                             |   12 +
 doc/webpage/index.html                             |  264 ++
 doc/webpage/p4est.css                              |   66 +
 example/balance_seeds/Makefile.am                  |   24 +
 example/balance_seeds/balance_seeds2.c             |  185 +
 example/balance_seeds/balance_seeds3.c             |   25 +
 example/mesh/Makefile.am                           |   21 +
 example/mesh/conndebug.p8c                         |  Bin 0 -> 1264 bytes
 example/mesh/mesh2.c                               |  478 +++
 example/mesh/mesh3.c                               |   38 +
 example/mesh/periodicity3.c                        |   72 +
 example/p6est/Makefile.am                          |   25 +
 example/p6est/p6est.c                              | 2337 +++++++++++
 example/p6est/p6est.h                              |  605 +++
 example/p6est/p6est_extended.h                     |  266 ++
 example/p6est/p6est_ghost.c                        |  625 +++
 example/p6est/p6est_ghost.h                        |  198 +
 example/p6est/p6est_io.c                           |    0
 example/p6est/p6est_io.h                           |    0
 example/p6est/p6est_lnodes.c                       |  399 ++
 example/p6est/p6est_lnodes.h                       |  271 ++
 example/p6est/p6est_profile.c                      | 1246 ++++++
 example/p6est/p6est_profile.h                      |  105 +
 example/p6est/p6est_vtk.c                          |  775 ++++
 example/p6est/p6est_vtk.h                          |  173 +
 example/p6est/test/test_all.c                      |  422 ++
 example/points/Makefile.am                         |   18 +
 example/points/points2.c                           |  241 ++
 example/points/points3.c                           |   43 +
 example/simple/Makefile.am                         |   18 +
 example/simple/simple2.c                           |  370 ++
 example/simple/simple3.c                           |  332 ++
 example/steps/Makefile.am                          |   51 +
 example/steps/hole_2d_cubit.inp                    |  272 ++
 example/steps/hole_2d_cubit.jou                    |    9 +
 example/steps/hole_2d_gmsh.geo                     |   31 +
 example/steps/hole_2d_gmsh.inp                     |  301 ++
 example/steps/hole_3d_cubit.inp                    | 2664 +++++++++++++
 example/steps/hole_3d_cubit.jou                    |   10 +
 example/steps/hole_3d_gmsh.geo                     |   35 +
 example/steps/hole_3d_gmsh.inp                     | 3448 ++++++++++++++++
 example/steps/hw32.h                               |   88 +
 example/steps/p4est_step1.c                        |  188 +
 example/steps/p4est_step2.c                        |  159 +
 example/steps/p4est_step3.c                        | 1219 ++++++
 example/steps/p4est_step4.c                        |  951 +++++
 example/steps/p8est_step1.c                        |   37 +
 example/steps/p8est_step2.c                        |   30 +
 example/steps/p8est_step3.c                        |   44 +
 example/steps/p8est_step4.c                        |   37 +
 example/tetgen/Makefile.am                         |   31 +
 example/tetgen/p8est_box_tetgen.ele                | 1147 ++++++
 example/tetgen/p8est_box_tetgen.node               |  281 ++
 example/tetgen/read_conn2.c                        |   60 +
 example/tetgen/read_conn3.c                        |   24 +
 example/tetgen/read_tetgen.c                       |   90 +
 example/tetgen/write_conn2.c                       |   64 +
 example/tetgen/write_conn3.c                       |   26 +
 example/timings/Makefile.am                        |   41 +
 example/timings/bricks2.c                          |  171 +
 example/timings/bricks3.c                          |   25 +
 example/timings/loadconn2.c                        |  167 +
 example/timings/loadconn3.c                        |   25 +
 example/timings/perfscript.sh                      |   42 +
 example/timings/timana.awk                         |   21 +
 example/timings/timana.sh                          |   68 +
 example/timings/timings2.c                         |  734 ++++
 example/timings/timings3.c                         |   25 +
 example/timings/tsearch3.c                         | 1062 +++++
 example/timings/tsrana.awk                         |   29 +
 example/timings/tsrana.sh                          |   66 +
 p4estindent                                        |   40 +
 sc/AUTHORS                                         |   88 +
 sc/COPYING                                         |  502 +++
 sc/Doxyfile.in                                     | 2305 +++++++++++
 sc/INSTALL                                         |  291 ++
 sc/Makefile.am                                     |  115 +
 sc/Makefile.sc.pre.in                              |   22 +
 sc/NEWS                                            |    4 +
 sc/README                                          |   14 +
 sc/bootstrap                                       |   17 +
 ...e-04ae88702a9f6d21e8be9af5f12b70dc25d05b17.yaml |   31 +
 ...e-0abee8bb980e793066eac95db0b2c5ac946ce949.yaml |   33 +
 ...e-2b229897e97d73ad3313b39f5043221e399996f1.yaml |   37 +
 ...e-307055f1e9a4ef01d09267aae4e41d62b894606b.yaml |   24 +
 ...e-3a78f43954db1996755732df98db805415ab4ce8.yaml |   24 +
 ...e-498931301b13195e239d47941ee37086dc40cef6.yaml |   31 +
 ...e-50a26b60f1fbcd5195940ca0d6af13a16c4b9c6d.yaml |   20 +
 ...e-5af792eb4766555f1204743970c5d6a3e5e1eaf2.yaml |   20 +
 ...e-63fab9640a28e11bcebe98f9a18612aec9917cfa.yaml |   20 +
 ...e-7f4c9393e1e2f9d173c9633dd3e1a96b947f702a.yaml |   35 +
 ...e-82067d245dbf57b2a938b7c2f1e24ab49920737e.yaml |   34 +
 ...e-a1aa51e81a6f7c92ad91376ce9150ce05496049c.yaml |   26 +
 ...e-ba52b9a974843d380af0f183866ec47c43348bbe.yaml |   26 +
 ...e-be85cecaeb979aee5dd659e2d4da4c06961aaea8.yaml |   31 +
 ...e-d1c07123f50402055f9cc973e68872d7ed05fc7b.yaml |   56 +
 ...e-f06e2b2e596ab478be598cfcf380d688eb1fa8b6.yaml |   34 +
 ...e-f6e1a861093ae06fe8ea3826f4fbb3c4247dc2a8.yaml |   29 +
 ...e-fbfa7595e16582a783ea86bbedd1ae38049faecf.yaml |   22 +
 sc/bugs/project.yaml                               |   29 +
 sc/build-aux/git-version-gen                       |  162 +
 sc/build-aux/git2cl                                |  308 ++
 sc/config/ax_prefix_config_h.m4                    |  203 +
 sc/config/ax_split_version.m4                      |   38 +
 sc/config/sc_blas.m4                               |  274 ++
 sc/config/sc_builtin.m4                            |   71 +
 sc/config/sc_c_check_flag.m4                       |   90 +
 sc/config/sc_c_version.m4                          |   89 +
 sc/config/sc_cuda.m4                               |   42 +
 sc/config/sc_include.m4                            |  305 ++
 sc/config/sc_lapack.m4                             |  200 +
 sc/config/sc_lint.m4                               |   90 +
 sc/config/sc_mpi.m4                                |  400 ++
 sc/config/sc_package.m4                            |  149 +
 sc/config/sc_pthread.m4                            |   42 +
 sc/config/sc_trilinos.m4                           |  122 +
 sc/configure.ac                                    |  132 +
 sc/doc/author_seyboldt.txt                         |    1 +
 sc/doc/author_weischer.txt                         |    1 +
 sc/doc/coding_standards.txt                        |   32 +
 sc/doc/doxygen.css                                 | 1164 ++++++
 sc/doc/error_policies.txt                          |   24 +
 sc/doc/git/README                                  |    3 +
 sc/doc/git/hooks/pre-commit                        |   36 +
 sc/doc/mainpage.dox                                |   67 +
 sc/doc/portability/README                          |   22 +
 sc/doc/portability/isportm4.sh                     |   11 +
 sc/doc/portability/portitm4.sh                     |   21 +
 sc/doc/portability/testfile.m4                     |   24 +
 sc/example/bspline/Makefile.am                     |   16 +
 sc/example/bspline/bspline.c                       |  208 +
 sc/example/bspline/bspline_per.c                   |  209 +
 sc/example/bspline/example1.txt                    |   15 +
 sc/example/bspline/example2.txt                    |   15 +
 sc/example/bspline/example3.txt                    |   14 +
 sc/example/cuda/Makefile.am                        |   14 +
 sc/example/dmatrix/Makefile.am                     |    9 +
 sc/example/dmatrix/dmatrix.c                       |  168 +
 sc/example/function/Makefile.am                    |    9 +
 sc/example/function/function.c                     |   62 +
 sc/example/logging/Makefile.am                     |    9 +
 sc/example/logging/logging.c                       |   88 +
 sc/example/options/Makefile.am                     |   11 +
 sc/example/options/options.c                       |  129 +
 sc/example/options/options.ini                     |   11 +
 sc/example/options/preload.ini                     |    4 +
 sc/example/pthread/Makefile.am                     |   15 +
 sc/example/pthread/condvar.c                       |  309 ++
 sc/example/pthread/pthread.c                       |  152 +
 sc/example/warp/Makefile.am                        |    9 +
 sc/example/warp/warp.c                             |   53 +
 sc/iniparser/AUTHORS                               |    5 +
 sc/iniparser/LICENSE                               |   21 +
 sc/iniparser/Makefile.am                           |   21 +
 sc/iniparser/README                                |   12 +
 sc/iniparser/dictionary.c                          |  398 ++
 sc/iniparser/dictionary.h                          |  187 +
 sc/iniparser/iniexample.c                          |  100 +
 sc/iniparser/iniparser.c                           |  748 ++++
 sc/iniparser/iniparser.h                           |  328 ++
 sc/iniparser/twisted.ini                           |  131 +
 sc/libb64/AUTHORS                                  |    7 +
 sc/libb64/CHANGELOG                                |   16 +
 sc/libb64/LICENSE                                  |   29 +
 sc/libb64/Makefile.am                              |   24 +
 sc/libb64/README                                   |  142 +
 sc/libb64/b64dec.c                                 |   43 +
 sc/libb64/b64enc.c                                 |   46 +
 sc/libb64/cdecode.c                                |  102 +
 sc/libb64/cencode.c                                |  121 +
 sc/libb64/libb64.h                                 |  139 +
 sc/scindent                                        |   40 +
 sc/src/Makefile.am                                 |   53 +
 sc/src/sc.c                                        | 1074 +++++
 sc/src/sc.h                                        |  567 +++
 sc/src/sc_allgather.c                              |  166 +
 sc/src/sc_allgather.h                              |   57 +
 sc/src/sc_amr.c                                    |  293 ++
 sc/src/sc_amr.h                                    |  135 +
 sc/src/sc_avl.c                                    |  670 ++++
 sc/src/sc_avl.h                                    |  212 +
 sc/src/sc_blas.c                                   |   40 +
 sc/src/sc_blas.h                                   |  134 +
 sc/src/sc_bspline.c                                |  509 +++
 sc/src/sc_bspline.h                                |  167 +
 sc/src/sc_builtin/getopt.h                         |  177 +
 sc/src/sc_builtin/getopt_int.h                     |  130 +
 sc/src/sc_builtin/obstack.h                        |  509 +++
 sc/src/sc_containers.c                             | 1478 +++++++
 sc/src/sc_containers.h                             |  905 +++++
 sc/src/sc_dmatrix.c                                |  749 ++++
 sc/src/sc_dmatrix.h                                |  314 ++
 sc/src/sc_flops.c                                  |  130 +
 sc/src/sc_flops.h                                  |  108 +
 sc/src/sc_functions.c                              |  197 +
 sc/src/sc_functions.h                              |  106 +
 sc/src/sc_getopt.c                                 | 1112 ++++++
 sc/src/sc_getopt.h                                 |   37 +
 sc/src/sc_getopt1.c                                |   70 +
 sc/src/sc_io.c                                     |  509 +++
 sc/src/sc_io.h                                     |  266 ++
 sc/src/sc_keyvalue.c                               |  447 +++
 sc/src/sc_keyvalue.h                               |  108 +
 sc/src/sc_lapack.c                                 |   36 +
 sc/src/sc_lapack.h                                 |  126 +
 sc/src/sc_lua.h                                    |   56 +
 sc/src/sc_mpi.c                                    |  358 ++
 sc/src/sc_mpi.h                                    |  301 ++
 sc/src/sc_notify.c                                 |  433 ++
 sc/src/sc_notify.h                                 |   60 +
 sc/src/sc_obstack.c                                |  381 ++
 sc/src/sc_obstack.h                                |   37 +
 sc/src/sc_options.c                                | 1202 ++++++
 sc/src/sc_options.h                                |  292 ++
 sc/src/sc_ranges.c                                 |  330 ++
 sc/src/sc_ranges.h                                 |  114 +
 sc/src/sc_reduce.c                                 |  566 +++
 sc/src/sc_reduce.h                                 |   69 +
 sc/src/sc_search.c                                 |  142 +
 sc/src/sc_search.h                                 |   71 +
 sc/src/sc_sort.c                                   |  450 +++
 sc/src/sc_sort.h                                   |   45 +
 sc/src/sc_statistics.c                             |  415 ++
 sc/src/sc_statistics.h                             |  161 +
 sc/src/sc_warp.c                                   |  221 ++
 sc/src/sc_warp.h                                   |   56 +
 sc/test/Makefile.am                                |   54 +
 sc/test/test_allgather.c                           |  132 +
 sc/test/test_arrays.c                              |  231 ++
 sc/test/test_builtin.c                             |  160 +
 sc/test/test_dmatrix.c                             |  151 +
 sc/test/test_dmatrix_pool.c                        |   70 +
 sc/test/test_io_sink.c                             |   96 +
 sc/test/test_keyvalue.c                            |  221 ++
 sc/test/test_notify.c                              |   90 +
 sc/test/test_pqueue.c                              |  155 +
 sc/test/test_reduce.c                              |  109 +
 sc/test/test_search.c                              |   62 +
 sc/test/test_sort.c                                |  133 +
 sc/test/test_sortb.c                               |   87 +
 src/Makefile.am                                    |   70 +
 src/p4est.c                                        | 3632 +++++++++++++++++
 src/p4est.h                                        |  473 +++
 src/p4est_algorithms.c                             | 3378 ++++++++++++++++
 src/p4est_algorithms.h                             |  303 ++
 src/p4est_balance.c                                |  984 +++++
 src/p4est_balance.h                                |   64 +
 src/p4est_base.c                                   |  123 +
 src/p4est_base.h                                   |  442 +++
 src/p4est_bits.c                                   | 1776 +++++++++
 src/p4est_bits.h                                   |  604 +++
 src/p4est_communication.c                          |  593 +++
 src/p4est_communication.h                          |  159 +
 src/p4est_connectivity.c                           | 4164 ++++++++++++++++++++
 src/p4est_connectivity.h                           |  721 ++++
 src/p4est_extended.h                               |  308 ++
 src/p4est_geometry.c                               |  102 +
 src/p4est_geometry.h                               |   84 +
 src/p4est_ghost.c                                  | 3931 ++++++++++++++++++
 src/p4est_ghost.h                                  |  305 ++
 src/p4est_io.c                                     |  269 ++
 src/p4est_io.h                                     |   68 +
 src/p4est_iterate.c                                | 3309 ++++++++++++++++
 src/p4est_iterate.h                                |  293 ++
 src/p4est_lnodes.c                                 | 3482 ++++++++++++++++
 src/p4est_lnodes.h                                 |  365 ++
 src/p4est_mesh.c                                   |  858 ++++
 src/p4est_mesh.h                                   |  228 ++
 src/p4est_nodes.c                                  | 1526 +++++++
 src/p4est_nodes.h                                  |  175 +
 src/p4est_plex.c                                   | 1511 +++++++
 src/p4est_plex.h                                   |   83 +
 src/p4est_points.c                                 |  394 ++
 src/p4est_points.h                                 |   74 +
 src/p4est_search.c                                 |  613 +++
 src/p4est_search.h                                 |  156 +
 src/p4est_to_p8est.h                               |  409 ++
 src/p4est_vtk.c                                    |  949 +++++
 src/p4est_vtk.h                                    |  195 +
 src/p4est_wrap.c                                   |  542 +++
 src/p4est_wrap.h                                   |  197 +
 src/p8est.c                                        |   28 +
 src/p8est.h                                        |  473 +++
 src/p8est_algorithms.c                             |   73 +
 src/p8est_algorithms.h                             |  304 ++
 src/p8est_balance.c                                |   25 +
 src/p8est_balance.h                                |   72 +
 src/p8est_bits.c                                   |  635 +++
 src/p8est_bits.h                                   |  706 ++++
 src/p8est_communication.c                          |   25 +
 src/p8est_communication.h                          |  159 +
 src/p8est_connectivity.c                           | 1061 +++++
 src/p8est_connectivity.h                           |  877 +++++
 src/p8est_extended.h                               |  310 ++
 src/p8est_geometry.c                               |  300 ++
 src/p8est_geometry.h                               |  104 +
 src/p8est_ghost.c                                  |  280 ++
 src/p8est_ghost.h                                  |  304 ++
 src/p8est_io.c                                     |   26 +
 src/p8est_io.h                                     |   68 +
 src/p8est_iterate.c                                |   25 +
 src/p8est_iterate.h                                |  403 ++
 src/p8est_lnodes.c                                 |   25 +
 src/p8est_lnodes.h                                 |  404 ++
 src/p8est_mesh.c                                   |   25 +
 src/p8est_mesh.h                                   |  229 ++
 src/p8est_nodes.c                                  |   25 +
 src/p8est_nodes.h                                  |  219 +
 src/p8est_plex.c                                   |   25 +
 src/p8est_plex.h                                   |   83 +
 src/p8est_points.c                                 |   33 +
 src/p8est_points.h                                 |   74 +
 src/p8est_search.c                                 |   25 +
 src/p8est_search.h                                 |  159 +
 src/p8est_tets_hexes.c                             |  719 ++++
 src/p8est_tets_hexes.h                             |   87 +
 src/p8est_vtk.c                                    |   25 +
 src/p8est_vtk.h                                    |  195 +
 src/p8est_wrap.c                                   |   24 +
 src/p8est_wrap.h                                   |  184 +
 test/Makefile.am                                   |  160 +
 test/test_balance2.c                               |  203 +
 test/test_balance3.c                               |   25 +
 test/test_balance_seeds2.c                         |  639 +++
 test/test_balance_seeds3.c                         |   27 +
 test/test_balance_type2.c                          |  167 +
 test/test_balance_type3.c                          |   25 +
 test/test_brick2.c                                 |  493 +++
 test/test_brick3.c                                 |   25 +
 test/test_coarsen2.c                               |  335 ++
 test/test_coarsen3.c                               |   25 +
 test/test_comm.c                                   |  153 +
 test/test_conn_complete2.c                         |  110 +
 test/test_conn_complete3.c                         |   25 +
 test/test_conn_reduce2.c                           |   92 +
 test/test_conn_reduce3.c                           |   25 +
 test/test_edge_face_corners3.c                     |   95 +
 test/test_face_transform3.c                        |  152 +
 test/test_ghost2.c                                 |  405 ++
 test/test_ghost3.c                                 |   25 +
 test/test_hash.c                                   |  130 +
 test/test_iterate2.c                               | 1051 +++++
 test/test_iterate3.c                               |   27 +
 test/test_join2.c                                  |   64 +
 test/test_join3.c                                  |   25 +
 test/test_lnodes2.c                                |  984 +++++
 test/test_lnodes3.c                                |   27 +
 test/test_loadsave2.c                              |  350 ++
 test/test_loadsave3.c                              |   25 +
 test/test_order.c                                  |  407 ++
 test/test_partition2.c                             |  382 ++
 test/test_partition3.c                             |   25 +
 test/test_partition_corr2.c                        |  185 +
 test/test_partition_corr3.c                        |   25 +
 test/test_periodic3.c                              |  310 ++
 test/test_plex2.c                                  |  318 ++
 test/test_plex3.c                                  |   27 +
 test/test_quadrants2.c                             |  644 +++
 test/test_quadrants3.c                             |  693 ++++
 test/test_reorder2.c                               |   69 +
 test/test_reorder3.c                               |   25 +
 test/test_replace2.c                               |  157 +
 test/test_replace3.c                               |   25 +
 test/test_search2.c                                |  232 ++
 test/test_search3.c                                |   25 +
 test/test_valid2.c                                 |  212 +
 test/test_valid3.c                                 |   29 +
 test/test_wrap2.c                                  |  129 +
 test/test_wrap3.c                                  |   24 +
 456 files changed, 120498 insertions(+)

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..7375c24
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "sc"]
+	path = sc
+	url = https://github.com/cburstedde/libsc.git
+	# url = http://burstedde.ins.uni-bonn.de/repos/libsc.git
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..5296975
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,72 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*
+  Authors:
+
+  Carsten Burstedde <carsten at ices.utexas.edu>
+  Lucas C. Wilcox <lucasw at ices.utexas.edu>
+  Tobin Isaac <tisaac at ices.utexas.edu>
+ */
+
+/*
+  Contributors:
+
+  Johann Rudi <johann at ices.utexas.edu>
+  Johannes Holke <holke at ins.uni-bonn.de>
+  Ethan Alan Hereth <e.a.hereth at gmail.com>
+  Alex Fikl <alexfikl at gmail.com>
+  Hannes Frank <frank at iag.uni-stuttgart.de>
+*/
+
+/*
+  Thanks to:
+
+  Matthias Maier <matthias.maier at iwr.uni-heidelberg.de>
+  Fabian Hempert <fabian.hempert at de.bosch.com>
+*/
+
+/*
+  Acknowledgement and Disclaimer:
+
+  The development of p4est was partially supported by the US National Science
+  Foundation (NSF Grants No. OCI-0749334, CCF-0427985, CNS-0540372,
+  CNS-0619838, DMS-0724746, OPP-0941678) and the US Department of Energy (DOE
+  Grants No.  06ER25782, 08ER25860, SC0002710).  The authors thank the Texas
+  Advanced Computing Center (TACC) for providing them with access to the Ranger
+  supercomputer under NSF TeraGrid award MCA04N026, and the National Center for
+  Computational Science (NCCS) for early-user access to the Jaguar Cray XT5
+  supercomputer.  Any opinions, findings and conclusions or recomendations
+  expressed in the source code and documentation are those of the authors and
+  do not necessarily reflect the views of the National Science Foundation
+  (NSF).
+*/
+
+/*
+  The subdirectory sc contains the SC Library licensed under GNU LGPL.
+  See the files sc/AUTHORS and sc/COPYING for copyright information.
+
+  Several scripts under config are released under GNU GPL versions.
+  These are not compiled or linked with p4est, so we consider them
+  independent programs that are not part of p4est.
+*/
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  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.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+                     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
+convey 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 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This 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.
diff --git a/Doxyfile.in b/Doxyfile.in
new file mode 100644
index 0000000..183714e
--- /dev/null
+++ b/Doxyfile.in
@@ -0,0 +1,2310 @@
+# Doxyfile 1.8.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = @PACKAGE_NAME@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = @top_builddir@/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        = @top_srcdir@
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = YES
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = YES
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = @top_srcdir@/src \
+                         @top_srcdir@/doc/mainpage.dox \
+                         @top_srcdir@/example/steps \
+                         @top_srcdir@/sc/src/sc.h \
+                         @top_srcdir@/sc/src/sc_containers.h \
+                         @top_srcdir@/example/p6est
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        = SC_EXTERN_C_BEGIN \
+                         SC_EXTERN_C_END
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 3
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          = p4est_ \
+                         p8est_
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 150
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = @PACKAGE_PREFIX at _DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       = @top_builddir@/doxygen/@PACKAGE_NAME at .doxygen.tags
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..8b82ade
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,291 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007, 2008 Free Software Foundation, Inc.
+
+   This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+   Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package.  The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.
+
+     Running `configure' might take a while.  While running, it prints
+     some messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+  6. Often, you can also type `make uninstall' to remove the installed
+     files again.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you can use GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory.  After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+   On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor.  Like
+this:
+
+     ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CPP="gcc -E" CXXCPP="g++ -E"
+
+   This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
+
+Installation Names
+==================
+
+   By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Particular systems
+==================
+
+   On HP-UX, the default C compiler is not ANSI C compatible.  If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+     ./configure CC="cc -Ae"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+   On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `<wchar.h>' header file.  The option `-nodtk' can be used as
+a workaround.  If GNU CC is not installed, it is therefore recommended
+to try
+
+     ./configure CC="cc"
+
+and if that doesn't work, try
+
+     ./configure CC="cc -nodtk"
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on.  Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+   Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug.  Until the bug is fixed you can use this workaround:
+
+     CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+     Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+     Print a summary of the options unique to this package's
+     `configure', and exit.  The `short' variant lists options used
+     only in the top level, while the `recursive' variant lists options
+     also present in any nested packages.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--prefix=DIR'
+     Use DIR as the installation prefix.  *Note Installation Names::
+     for more details, including other options available for fine-tuning
+     the installation locations.
+
+`--no-create'
+`-n'
+     Run the configure checks, but stop before creating any output
+     files.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..3556c7a
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,120 @@
+
+# This file is part of p4est.
+# Makefile.am in toplevel directory
+
+ACLOCAL_AMFLAGS = -I config @P4EST_SC_AMFLAGS@
+if P4EST_SC_MK_USE
+ at P4EST_SC_MK_INCLUDE@
+endif
+
+# initialize empty variables
+AM_CPPFLAGS =
+CLEANFILES =
+DISTCLEANFILES =
+EXTRA_DIST =
+LDADD =
+LINT_CSOURCES =
+TESTS =
+bin_PROGRAMS =
+check_PROGRAMS =
+include_HEADERS =
+lib_LTLIBRARIES =
+nodist_include_HEADERS =
+noinst_HEADERS =
+noinst_PROGRAMS =
+sysconf_DATA =
+dist_p4estdata_DATA =
+
+# use this if you want to link in p4est without autotools
+sysconf_DATA += Makefile.p4est.mk
+CLEANFILES += Makefile.p4est.mk
+Makefile.p4est.mk : Makefile.p4est.pre
+	cat $< | \
+        sed -e 's,{\(\(.*prefix\|sysconfdir\)\)},{p4est_\1},g' \
+            -e 's,^\(\(.*prefix\|sysconfdir\) *=\),p4est_\1,g' > $@
+
+# install p4est m4 macros in the correct directory
+p4estaclocaldir = $(datadir)/aclocal
+dist_p4estaclocal_DATA = \
+        config/p4est_include.m4 config/p4est_metis.m4
+
+# install p4est data files in the correct directory
+p4estdatadir = $(datadir)/data
+
+# setup test environment
+if P4EST_MPIRUN
+LOG_COMPILER = @P4EST_MPIRUN@
+AM_LOG_FLAGS = @P4EST_MPI_TEST_FLAGS@
+endif
+
+# recurse only into subpackages
+SUBDIRS = @P4EST_SC_SUBDIR@
+DIST_SUBDIRS = $(SUBDIRS)
+
+# handle toplevel directory
+EXTRA_DIST += \
+        bootstrap p4estindent build-aux/git-version-gen build-aux/git2cl bugs doc
+DISTCLEANFILES += \
+        _configs.sed src/p4est_config.h @P4EST_DISTCLEAN@
+.PHONY: lint ChangeLog
+
+# non-recursive build
+include src/Makefile.am
+include test/Makefile.am
+include example/mesh/Makefile.am
+include example/points/Makefile.am
+include example/simple/Makefile.am
+include example/steps/Makefile.am
+include example/tetgen/Makefile.am
+include example/timings/Makefile.am
+include example/balance_seeds/Makefile.am
+
+# lint static syntax checker
+ALL_LINT_FLAGS = $(LINT_FLAGS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+                 $(MPI_INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) \
+                 $(src_libp4est_a_CPPFLAGS)
+lint:
+if LINT
+	@for subdir in $(SUBDIRS) ; do \
+		echo "Making $@ in $$subdir"; \
+		(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) lint) ; \
+	done
+	for f in $(LINT_CSOURCES) ; do \
+		$(LINT) $(ALL_LINT_FLAGS) $(top_srcdir)/$$f || \
+		echo "Lint check failed for $$f" ; \
+	done
+else
+	@echo "Static source code check disabled by configure"
+endif
+
+# revision control and ChangeLog
+ChangeLog:
+	(GIT_DIR=@top_srcdir@/.git git log > .ChangeLog.tmp && \
+         cat .ChangeLog.tmp | @top_srcdir@/build-aux/git2cl > ChangeLog) ; \
+        rm -f .ChangeLog.tmp
+
+dist-hook:
+	echo $(VERSION) > $(distdir)/.tarball-version
+	test "x$(VERSION)" = "x`@top_srcdir@/build-aux/git-version-gen\
+              @top_srcdir@/.tarball-version`" || \
+        ((echo "Stale version;" ; echo "Please run:" ; \
+          echo "     cd @top_srcdir@ && ./bootstrap" ; \
+          echo "before make dist") 1>&2 ; rm -r $(distdir) ; exit 1)
+if P4EST_DIST_DENY
+	@echo "-----------------------------------------------------"
+	@echo "make dist does not work with external libsc"
+	@echo "-----------------------------------------------------"
+	rm -rf $(distdir)
+	@exit 1
+endif
+
+clean-local: clean-local-p6est
+	rm -f ChangeLog *vtu *.visit *.p4c *.p4p *.p8c *.p8p
+
+maintainer-clean-local:
+	rm -rf doxygen
+
+doxygen: Doxyfile
+	doxygen
+
+.PHONY: doxygen
diff --git a/Makefile.p4est.pre.in b/Makefile.p4est.pre.in
new file mode 100644
index 0000000..5c1dab8
--- /dev/null
+++ b/Makefile.p4est.pre.in
@@ -0,0 +1,24 @@
+
+# This file is part of p4est
+# Use `include /path/to/Makefile.p4est.mk' in your Makefile
+# to use p4est in your project without autotools
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+p4est_sysconfdir = @sysconfdir@
+
+ at P4EST_SC_MK_INCLUDE@
+
+# P4EST_CC and P4EST_CFLAGS may not be necessary for your project
+P4EST_CC = @CC@
+P4EST_CFLAGS = @CFLAGS@
+
+# These pull in p4est but none of its dependencies
+P4EST_PKG_CPPFLAGS = -I at includedir@
+P4EST_PKG_LDFLAGS = -L at libdir@
+P4EST_PKG_LIBS = -lp4est
+
+# These pull in everything needed by p4est
+P4EST_CPPFLAGS = @CPPFLAGS@ $(SC_PKG_CPPFLAGS) $(P4EST_PKG_CPPFLAGS)
+P4EST_LDFLAGS = @LDFLAGS@ $(SC_PKG_LDFLAGS) $(P4EST_PKG_LDFLAGS)
+P4EST_LIBS = $(P4EST_PKG_LIBS) $(SC_PKG_LIBS) @LIBS@
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..8e6485b
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,4 @@
+
+IMPORTANT NOTE:
+Our official tarballs are linked from http://www.p4est.org/.
+The archives generated by github will not work; see the README file.
diff --git a/README b/README
new file mode 100644
index 0000000..2634b48
--- /dev/null
+++ b/README
@@ -0,0 +1,172 @@
+
+This is the README file for p4est.
+
+p4est is a C library to manage a collection (a forest) of multiple
+connected adaptive quadtrees or octrees in parallel.
+
+p4est is written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+and released under the GNU General Public Licence version 2 (or later),
+Copyright (C) 2010 The University of Texas System.
+
+The official web page for source code and documentation is www.p4est.org.
+Please send bug reports and ideas for contribution to p4est at librelist.com.
+
+
+ 0. Acknowledgement and Disclaimer
+
+The development of p4est was partially supported by the US National Science
+Foundation (NSF Grants No. OCI-0749334, CCF-0427985, CNS-0540372, CNS-0619838,
+DMS-0724746, OPP-0941678) and the US Department of Energy (DOE Grants No.
+06ER25782, 08ER25860, SC0002710).  The authors thank the Texas Advanced
+Computing Center (TACC) for providing them with access to the Ranger
+supercomputer under NSF TeraGrid award MCA04N026, and the National Center for
+Computational Science (NCCS) for early-user access to the Jaguar Cray XT5
+supercomputer.  Any opinions, findings and conclusions or recomendations
+expressed in the source code and documentation are those of the authors and do
+not necessarily reflect the views of the National Science Foundation (NSF).
+
+
+ 1. Purpose
+
+Many applications in applied mathematics and numerical simulation use a mesh of
+computational cells that covers the domain of interest.  The numerical solution
+is then approximated by functions, each of which is associated with a small set
+of cells (or even one).  Dynamic ``adaptive'' methods change the mesh during
+the simulation by local refinement or coarsening, and ``parallel'' methods
+distribute (``partition'') the mesh between multiple processors, where each
+processor ideally receives an equal share of the computational load.  p4est
+isolates the task of parallel dynamic adaptive mesh refinement (AMR),
+coarsening, and load balancing, and encapsulates algorithms that scale well to
+large numbers (10^5) of processors.
+
+These algorithms are predominantly used for the numerical solution of partial
+differential equations, but also support various other tasks where fast
+hierarchical space subdivision is required, for example to locate particles in
+space or to organize, sort, and search in large data sets.
+
+
+ 2. Geometric structure
+
+The basic structure used in p4est is a ``connectivity'' of quadtrees (2D) or
+octrees (3D) that covers the domain of interest in a conforming macro-mesh.
+This includes the case of using exactly one tree for representing the
+hypercube.  The trees can be arbitrarily refined and coarsened, where the
+storage of quadrants/octants is distributed in parallel.  Thus, the mesh
+primitives used in p4est are quadrilaterals in 2D and hexahedra in 3D.  The
+adaptive structure allows for quadrants/octants of different sizes to neighbor
+each other, which is commonly called ``non-conforming''.  This concept leads to
+``hanging'' faces and edges.
+
+
+ 3. Core p4est (2D) and p8est (3D) routines
+
+p?est_new: Create an equi-partitioned, uniformly refined forest.
+
+p?est_refine: Adaptively subdivide octants based on a callback function, once
+for each octant or recursively.
+
+p?est_coarsen: Replace families of eight child octants by their common parent
+octant, once or recursively.
+
+p?est_partition: Redistribute the octants in parallel, according to a given
+target number of octants for each process, or weights prescribed for all
+octants.
+
+p?est_balance: Ensure at most 2:1 size relations between neighboring octants by
+local refinement where necessary.
+
+p?est_checksum: Compute a partition-independent integer ``fingerprint'' of a
+forest.  This is useful for debugging and regression testing.
+
+
+ 4. Interfacing to p4est from applications
+
+p?est_ghost: Collect one layer of off-process octants touching the process
+boundaries from the outside.  This function requires a previous call to
+p?est_balance.  This is the most generally useful function for external
+applications.  By querying the ghost layer, the application can associate
+degrees of freedom with the mesh which are the basis for all numerical
+computation.
+
+p?est_nodes: Create a globally unique numbering of the mesh nodes (i.e., the
+vertices at the corners of octants, not to be confused with octree nodes),
+taking into account the classification into ``independent'' and ``hanging''
+nodes.  This function requires previous calls to p?est_balance and p?est_ghost.
+It provides a global numbering for degrees of freedom that can be associated
+with piecewise linear finite elements.
+
+p?est_lnodes: Like p?est_nodes, but for arbitrary order Lagrangian basis
+functions.  Supersedes p?est_nodes for the piecewise linear special case.
+
+
+ 5. Installation from a release tarball
+
+IMPORTANT NOTE:
+Our official tarballs are linked from http://www.p4est.org/ and work fine.
+The tarballs under
+   https://github.com/cburstedde/p4est/releases/
+are created by github and are NOT endorsed by us.  In particular, they are
+missing the subdirectory sc, the configure script, and other generated files.
+
+p4est uses the GNU autoconf/automake/libtool build system.  See the INSTALL
+file for details.  Our official tarballs contain the configure script and all
+required files.  The following configure options are commonly used.
+
+--enable-mpi is necessary for parallel functionality.  Without this option,
+p4est does not require MPI, which is replaced by a dummy implementation that
+always assumes an MPI_Comm_size of 1 and an MPI_Comm_rank of 0.
+
+--enable-debug enables assertions and additional code for verification.  This
+is generally helpful for development, even if it is somewhat slower and
+produces a lot of diagnostic log messages.
+
+CFLAGS="-Wall -g -O0" is an example of setting flags for a development build
+compiled with the GNU compilers.  p4est is written in plain C and does not
+require FFLAGS or CXXFLAGS.
+
+Both in-source and out-of-source builds are supported.  For a one-time
+installation, ./configure can be called in the root directory of the unpacked
+tree.  For development or for testing multiple configure options, create an
+empty directory for each configuration and invoke configure with a relative
+path.
+
+The subpackage 'sc' is contained in the tarball and used by default.  It is
+possible to use a version of sc that has been make install'd independently:
+(1.) Create an empty directory and call sc/configure with its relative path.
+     Use --prefix=path-to-install-sc to specify the destination for the
+     compiled files.  Then call make install.
+(2.) Create another empty directory and call the p4est configure with its
+     relative path and the options --prefix=path-to-install-p4est and
+     --with-sc=path-to-install-sc.  Make sure to use the same compiler flags
+     and configure options as in (1.).  Finally, call make install.
+This is the proposed procedure to split the p4est installation into two
+packages, say for a linux binary distribution.  The benefit is that other
+packages that might require sc do not force p4est to get installed.
+
+
+ 6. Installation from source
+
+When installing from source, GNU autotools must be invoked to generate the
+necessary configuration files and the configure script.  A bootstrap script is
+provided for this purpose: give the shell command './bootstrap' before
+following the instructions in the INSTALL file.  Then proceed as in 5.
+
+
+ 7. Installation from git repository
+
+If you have obtained p4est from a git repository, such as via the shell command
+   git clone https://github.com/cburstedde/p4est.git
+then the libsc submodule, which resides in the subdirectory sc, must be
+downloaded before configuring and compiling.  This can be accomplished with the
+shell command 'git submodule init; git submodule update'.  After the submodule
+has been obtained, you can install from source as described above in 6.
+
+
+ 8. Using p4est through the deal.II interface
+
+The recent development version of the generic adaptive finite element software
+library deal.II interfaces to p4est to parallelize its mesh handling.  The most
+convenient way to compile and install p4est to be accessible for deal.II is to
+use the doc/p4est-setup.sh script.  This creates both a debug and production
+version compiled with minimal logging output.  To know what is going on within
+p4est, the log level needs to be relaxed in the script.
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 0000000..2bed6a0
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,39 @@
+#! /bin/sh
+
+# Call this without arguments if p4est is the toplevel package.
+# Call this with one argument if p4est is itself a subdirectory:
+# ./bootstrap <sc config directory>
+
+# override by option argument
+SC_CONFIG="sc/config"
+if test -n "$1" ; then
+  SC_CONFIG="$1"
+fi
+if test ! -d "$SC_CONFIG" ; then
+  echo "Error: Cannot find directory $SC_CONFIG"
+  echo "   If you just called ./bootstrap from a fresh clone of p4est"
+  echo "   you may need to checkout the submodule sc:"
+  echo "   \$ git submodule init && git submodule update"
+  exit 1
+fi
+# convert into an absolute path
+SC_CONFIG=`cd "$SC_CONFIG" && pwd`
+
+if test -x "sc/bootstrap" ; then
+  echo "Running bootstrap in subdirectory sc"
+  (cd sc && ./bootstrap)
+fi
+
+echo "--- This is the bootstrap script for p4est ---"
+echo "Current directory is $PWD"
+
+LIBTOOLIZE=`which glibtoolize`
+if test ! -x "$LIBTOOLIZE" ; then LIBTOOLIZE=`which libtoolize` ; fi
+if test ! -x "$LIBTOOLIZE" ; then echo "bootstrap requires libtoolize" ; \
+   exit 1 ; fi
+
+aclocal -Wall -I config -I "$SC_CONFIG"
+autoconf -Wall --force
+autoheader -Wall --force
+"$LIBTOOLIZE" --install --copy
+automake -Wall --add-missing --copy
diff --git a/bugs/issue-186d1690026feedd7633c97839bf443b534e6f70.yaml b/bugs/issue-186d1690026feedd7633c97839bf443b534e6f70.yaml
new file mode 100644
index 0000000..f96c018
--- /dev/null
+++ b/bugs/issue-186d1690026feedd7633c97839bf443b534e6f70.yaml
@@ -0,0 +1,25 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Compute minimal required edge and corner connectivity from faces
+desc: |-
+  Currently edge and corner connectivities need to be specified manually.
+  It is possible to infer this information from the face connectivity.
+  Only for weird setups the manual information could be a superset of this.
+type: :feature
+component: p4est
+release: "1.4"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :reorg
+creation_time: 2008-10-31 02:42:27.098035 Z
+references: []
+
+id: 186d1690026feedd7633c97839bf443b534e6f70
+log_events: 
+- - 2008-10-31 02:42:28.402874 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2010-07-05 20:15:47.119808 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition reorg
+  - This will be addressed in {issue d0e99b9f6d2204e88c8e00a2c0812ae65c2910d5} with lua code.
diff --git a/bugs/issue-19d672a00739ace2908339f96aa340ce1097f02b.yaml b/bugs/issue-19d672a00739ace2908339f96aa340ce1097f02b.yaml
new file mode 100644
index 0000000..38eb9a5
--- /dev/null
+++ b/bugs/issue-19d672a00739ace2908339f96aa340ce1097f02b.yaml
@@ -0,0 +1,40 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Assertion in p8est_nodes_new
+desc: |-
+  reproducible with
+  
+  mpiexec -n 27 p8est_timings -c periodic --level 3
+  
+  [libsc 22] Abort: Assertion '(k < rank && il < offset_owned_indeps) || (k > rank && il >= end_owned_indeps)'
+  [libsc 22] Abort: /h2/tisaac/Projects/p4est/src/p4est_nodes.c:1183
+  
+  I found a smaller example
+  
+  mpiexec -n 7 p8est_timings -c periodic --level 2
+type: :bugfix
+component: p4est
+release: 
+reporter: Tobin Isaac <tisaac at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2014-03-04 04:17:58.885803 Z
+references: []
+
+id: 19d672a00739ace2908339f96aa340ce1097f02b
+log_events: 
+- - 2014-03-04 04:18:01.408539 Z
+  - Tobin Isaac <tisaac at ices.utexas.edu>
+  - created
+  - ""
+- - 2014-03-05 05:20:24.872509 Z
+  - Tobin Isaac <tisaac at ices.utexas.edu>
+  - edited description
+  - ""
+- - 2014-03-05 17:50:05.310170 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - Fixed a bug in p4est_nodes, the examples run cleanly now.
+- - 2014-03-18 10:14:10.268451 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition fixed
+  - The program does not crash anymore.
diff --git a/bugs/issue-1bcc4bde086f347681a9c33d7e395d6718ae1883.yaml b/bugs/issue-1bcc4bde086f347681a9c33d7e395d6718ae1883.yaml
new file mode 100644
index 0000000..5f889ac
--- /dev/null
+++ b/bugs/issue-1bcc4bde086f347681a9c33d7e395d6718ae1883.yaml
@@ -0,0 +1,43 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: hot induced Assertion 'found'
+desc: |-
+  If rhea r4865 is comiled with p4est then the following run
+  of hot will crash.
+  
+    mpirun -np 4 ./hot sphere 3 uniform 0 6 1
+  
+  It crashes with the following message
+  
+  ...
+  [p4est 3] Peer ranges 1/2/25 first 0 last 2 owned 2900/3460
+  [p4est 3] Node queries send 3 nonz 3 recv 1
+  Abort: Assertion 'found'   in src/p4est_mesh.c:1101
+  Abort: Assertion 'found'   in src/p4est_mesh.c:1101
+  Abort: Assertion 'found'   in src/p4est_mesh.c:1101
+  Abort: Assertion 'found'   in src/p4est_mesh.c:1101
+  ...
+type: :bugfix
+component: p4est
+release: "0.3"
+reporter: Lucas C Wilcox <lucasw at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-11-03 21:30:41.329341 Z
+references: []
+
+id: 1bcc4bde086f347681a9c33d7e395d6718ae1883
+log_events: 
+- - 2008-11-03 21:30:43.150090 Z
+  - Lucas C Wilcox <lucasw at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-11-04 04:13:51.758700 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - |-
+    The bug was caused by calling p4est_nodes_new in Rhea.
+    This function requires a ghost layer across corners.
+    Rhea was using BALANCE_EDGE which currently does not provide
+    all ghosts across corners. Fixed in Rhea by using BALANCE_FULL.
+    Once {issue 84a74da0a3a4f357666853d81ce8280d22d3d6e1} is resolved,
+    can be changed back in Rhea to BALANCE_EDGE.
diff --git a/bugs/issue-1fa6eb95c70d012c57d06e3683c0ac6a393ccc7b.yaml b/bugs/issue-1fa6eb95c70d012c57d06e3683c0ac6a393ccc7b.yaml
new file mode 100644
index 0000000..ab03054
--- /dev/null
+++ b/bugs/issue-1fa6eb95c70d012c57d06e3683c0ac6a393ccc7b.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Check edge and corner connectivity for minimum requirements
+desc: |-
+  The minimum required edge and corner connectivities can be computed
+  from the face connectivity alone. Make this a standard assertion.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2008-10-31 02:44:12.628192 Z
+references: []
+
+id: 1fa6eb95c70d012c57d06e3683c0ac6a393ccc7b
+log_events: 
+- - 2008-10-31 02:44:13.179011 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2010-01-22 17:46:03.766132 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 0.3
+  - ""
diff --git a/bugs/issue-2b15a0df0596919470477c98141355b80cab53bc.yaml b/bugs/issue-2b15a0df0596919470477c98141355b80cab53bc.yaml
new file mode 100644
index 0000000..3f0e041
--- /dev/null
+++ b/bugs/issue-2b15a0df0596919470477c98141355b80cab53bc.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Remove the speed penalty for balancing many trees
+desc: |-
+  Currently the balance algorithm has run time O(nt**2) where nt is the
+  number of trees. This can be changed to O(nt).
+type: :bugfix
+component: p4est
+release: "0.3"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-16 17:42:15.248214 Z
+references: []
+
+id: 2b15a0df0596919470477c98141355b80cab53bc
+log_events: 
+- - 2008-10-16 17:42:16.544013 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-10-31 19:37:59.486704 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - The unintentionally tree-squared algorithm is now quadrant-linear.
diff --git a/bugs/issue-2ee86494dff916b22697a711be761c6e4278b349.yaml b/bugs/issue-2ee86494dff916b22697a711be761c6e4278b349.yaml
new file mode 100644
index 0000000..8984438
--- /dev/null
+++ b/bugs/issue-2ee86494dff916b22697a711be761c6e4278b349.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Move sc_fread and sc_fwrite into libsc
+desc: |-
+  The files p4est_connectivity.c and p8est_connectivity.c contain
+  static functions sc_fread and sc_fwrite. These shoule be in libsc.
+type: :bugfix
+component: p4est
+release: "0.3"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-17 01:45:43.157666 Z
+references: []
+
+id: 2ee86494dff916b22697a711be761c6e4278b349
+log_events: 
+- - 2008-10-17 01:45:44.917608 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-10-17 16:40:51.694722 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - The functions are now in sc_io.{c,h}.
diff --git a/bugs/issue-3209cbc7fe045c201f5f0b4af7951a3b81c164ab.yaml b/bugs/issue-3209cbc7fe045c201f5f0b4af7951a3b81c164ab.yaml
new file mode 100644
index 0000000..81e99c5
--- /dev/null
+++ b/bugs/issue-3209cbc7fe045c201f5f0b4af7951a3b81c164ab.yaml
@@ -0,0 +1,36 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Crash in p8est_timings
+desc: |-
+  The following call crashes with double-free/corruption:
+  mpirun -np 8 example/timings/p8est_timings -c twocubes -l 5
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :closed
+disposition: :fixed
+creation_time: 2014-02-14 10:22:28.844116 Z
+references: []
+
+id: 3209cbc7fe045c201f5f0b4af7951a3b81c164ab
+log_events: 
+- - 2014-02-14 10:22:29.345901 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
+- - 2014-02-14 10:27:35.658114 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    We actually hit an assertion:
+    [p4est] Into p8est_lnodes_new
+    [libsc 4] Abort: Assertion 'nproc != rank'
+    [libsc 4] Abort: /home/carsten/work/p4est/src/p4est_lnodes.c:921
+- - 2014-02-14 14:19:21.305941 Z
+  - Tobin Isaac <tisaac at ices.utexas.edu>
+  - commented
+  - I fixed the assertion, but there is still a checksum mismatch.
+- - 2014-02-14 15:34:12.672817 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition fixed
+  - The checksums are clean up and cherry-picked.
diff --git a/bugs/issue-327c7e8894b128ebd016656f8f4ba10e34f6f622.yaml b/bugs/issue-327c7e8894b128ebd016656f8f4ba10e34f6f622.yaml
new file mode 100644
index 0000000..88b1f30
--- /dev/null
+++ b/bugs/issue-327c7e8894b128ebd016656f8f4ba10e34f6f622.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Remove MPI_COMM_NULL checks
+desc: |-
+  It is currently allowed to pass MPI_COMM_NULL to the p4est.
+  This is no longer necessary since libsc defines MPI_COMM_WORLD if missing.
+type: :bugfix
+component: p4est
+release: "0.3"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-11-24 00:02:57.414561 Z
+references: []
+
+id: 327c7e8894b128ebd016656f8f4ba10e34f6f622
+log_events: 
+- - 2008-11-24 00:02:58.038546 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-11-30 01:27:09.143109 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - This is done in current libsc and p4est.
diff --git a/bugs/issue-3bfc08f604a148d0535c01dd830f1eadc7933ecd.yaml b/bugs/issue-3bfc08f604a148d0535c01dd830f1eadc7933ecd.yaml
new file mode 100644
index 0000000..23ea5d6
--- /dev/null
+++ b/bugs/issue-3bfc08f604a148d0535c01dd830f1eadc7933ecd.yaml
@@ -0,0 +1,18 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Additional verification
+desc: Verify nodes agains lnodes.  Verify p4est_quadrant_is_inside_tree again.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-07-02 14:30:38.610317 Z
+references: []
+
+id: 3bfc08f604a148d0535c01dd830f1eadc7933ecd
+log_events: 
+- - 2014-07-02 14:30:39.426218 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
diff --git a/bugs/issue-3c9b2ae49358d99afd662085c2bd2d5772309e56.yaml b/bugs/issue-3c9b2ae49358d99afd662085c2bd2d5772309e56.yaml
new file mode 100644
index 0000000..e4ef97c
--- /dev/null
+++ b/bugs/issue-3c9b2ae49358d99afd662085c2bd2d5772309e56.yaml
@@ -0,0 +1,30 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Replace global_last_quad_index in p4est_partition_given
+desc: |-
+  This variable has been replaced by global_first_quadrant
+  everywhere else in the code.  That's easier to use since it has
+  mpisize + 1 elements.  It would be most consistent to also
+  make the end_* variables in partition_given exclusive.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :wontfix
+creation_time: 2008-11-29 21:27:18.305382 Z
+references: []
+
+id: 3c9b2ae49358d99afd662085c2bd2d5772309e56
+log_events: 
+- - 2008-11-29 21:27:19.457242 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2010-01-22 17:46:15.589709 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 0.3
+  - ""
+- - 2010-07-07 15:32:54.619488 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition wontfix
+  - It works as it is and in quite understandable.
diff --git a/bugs/issue-3ca5f0c70f57849b33c416465abe25c00db8f3af.yaml b/bugs/issue-3ca5f0c70f57849b33c416465abe25c00db8f3af.yaml
new file mode 100644
index 0000000..add034d
--- /dev/null
+++ b/bugs/issue-3ca5f0c70f57849b33c416465abe25c00db8f3af.yaml
@@ -0,0 +1,18 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: The non-MPI compile throws a couple warnings.  Check non-debug too.
+desc: ""
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-04-08 12:28:55.445223 Z
+references: []
+
+id: 3ca5f0c70f57849b33c416465abe25c00db8f3af
+log_events: 
+- - 2014-04-08 12:28:56.069153 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
diff --git a/bugs/issue-3cd73c7e60fb75ee11ee649343679880063eda77.yaml b/bugs/issue-3cd73c7e60fb75ee11ee649343679880063eda77.yaml
new file mode 100644
index 0000000..7474629
--- /dev/null
+++ b/bugs/issue-3cd73c7e60fb75ee11ee649343679880063eda77.yaml
@@ -0,0 +1,31 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Remove unnecessary global communication
+desc: |-
+  Most of the p4est_comm_count_quadrants calls are not needed.
+  Remove them and introduce a count_dirty flag or similar.
+  The counts are communicated only when needed.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2008-11-25 01:31:59.556735 Z
+references: []
+
+id: 3cd73c7e60fb75ee11ee649343679880063eda77
+log_events: 
+- - 2008-11-25 01:32:00.068653 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-07-07 02:18:50.085842 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - commented
+  - |-
+    The allgather in comm_count_quadrants uses gloidx instead of locidx.
+    Fix and possibly aggregate it with comm_global_partition.
+- - 2010-01-22 17:46:17.813592 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 0.3
+  - ""
diff --git a/bugs/issue-40109afca06b4e94bbb61bb2949afca54a85da45.yaml b/bugs/issue-40109afca06b4e94bbb61bb2949afca54a85da45.yaml
new file mode 100644
index 0000000..2913cd7
--- /dev/null
+++ b/bugs/issue-40109afca06b4e94bbb61bb2949afca54a85da45.yaml
@@ -0,0 +1,22 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: abstract distributed set communication in libsc
+desc: The communication routines in lnodes should be abstracted into an object in libsc that deals with distributed index sets and communicating data associated with those index sets
+type: :task
+component: p4est
+release: 
+reporter: Tobin Isaac <tisaac at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2014-01-24 04:51:25.965937 Z
+references: []
+
+id: 40109afca06b4e94bbb61bb2949afca54a85da45
+log_events: 
+- - 2014-01-24 04:51:34.546223 Z
+  - Tobin Isaac <tisaac at ices.utexas.edu>
+  - created
+  - ""
+- - 2014-01-28 17:11:50.638256 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - Feel free to propose some prototypes.  Could be interesting.
diff --git a/bugs/issue-47e1146b570aa7160ad7bbff091a220a4e3f8800.yaml b/bugs/issue-47e1146b570aa7160ad7bbff091a220a4e3f8800.yaml
new file mode 100644
index 0000000..0b2d9f2
--- /dev/null
+++ b/bugs/issue-47e1146b570aa7160ad7bbff091a220a4e3f8800.yaml
@@ -0,0 +1,26 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Implement geometry transformations in 2D
+desc: |-
+  Implement geometry transforms in 2D VTK output.
+  Do this analogous to the 3D case and allow for the scale parameter.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :wontfix
+creation_time: 2009-03-19 19:51:52.151946 Z
+references: []
+
+id: 47e1146b570aa7160ad7bbff091a220a4e3f8800
+log_events: 
+- - 2009-03-19 19:51:52.959774 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2010-09-29 15:32:52.459831 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition wontfix
+  - |-
+    The p4est_geometry interface will be removed.
+    It will be replaced by a generic transfinite blending scheme.
diff --git a/bugs/issue-4c0103ff75bb0fc5249a6552c6ac3634f8597a6a.yaml b/bugs/issue-4c0103ff75bb0fc5249a6552c6ac3634f8597a6a.yaml
new file mode 100644
index 0000000..c09622c
--- /dev/null
+++ b/bugs/issue-4c0103ff75bb0fc5249a6552c6ac3634f8597a6a.yaml
@@ -0,0 +1,25 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Implement neighborhood structure
+desc: |-
+  Collect all relevant neighborhood information and store it
+  in a concise set of C structures.  This should be all that's needed
+  to create arbitrary numberings of degrees of freedom.
+type: :feature
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-02-11 22:31:56.780589 Z
+references: []
+
+id: 4c0103ff75bb0fc5249a6552c6ac3634f8597a6a
+log_events: 
+- - 2009-02-11 22:31:57.916401 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-11-02 21:28:59.996252 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - We have the iterator now which should provide sufficient functionality.
diff --git a/bugs/issue-5297112884f1f36a0a9dd9bec2de90481ccdf7b7.yaml b/bugs/issue-5297112884f1f36a0a9dd9bec2de90481ccdf7b7.yaml
new file mode 100644
index 0000000..54f6dab
--- /dev/null
+++ b/bugs/issue-5297112884f1f36a0a9dd9bec2de90481ccdf7b7.yaml
@@ -0,0 +1,28 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Create p4est connectivity structure from common mesh formats
+desc: |-
+  Survey external mesh storage formats that describe conforming hex meshes.
+  Write conversion code/scripts to turn them into a p4est_connectivity_t.
+type: :feature
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :closed
+disposition: :fixed
+creation_time: 2014-03-27 09:52:29.635056 Z
+references: []
+
+id: 5297112884f1f36a0a9dd9bec2de90481ccdf7b7
+log_events: 
+- - 2014-03-27 09:52:30.275027 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
+- - 2014-07-21 09:08:29.886199 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - We have the abaqus reader now.  Robustify and then close.
+- - 2014-07-25 08:33:38.109922 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition fixed
+  - This is done -- basic safety checks are in place.
diff --git a/bugs/issue-52b927ff7d7bab4ea8f05c9d153dddba25199564.yaml b/bugs/issue-52b927ff7d7bab4ea8f05c9d153dddba25199564.yaml
new file mode 100644
index 0000000..64bbf0b
--- /dev/null
+++ b/bugs/issue-52b927ff7d7bab4ea8f05c9d153dddba25199564.yaml
@@ -0,0 +1,22 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Library versioning
+desc: |-
+  We might want to switch to the API versioning scheme in
+  https://sourceware.org/autobook/autobook/autobook_91.html#SEC91
+  Even though package versions and API versions do not need to coincide,
+  we could switch to that convention starting with 1.0.0 for July 1st.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-05-02 14:49:24.800135 Z
+references: []
+
+id: 52b927ff7d7bab4ea8f05c9d153dddba25199564
+log_events: 
+- - 2014-05-02 14:49:25.814381 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
diff --git a/bugs/issue-6c6239e053919531627e706685ed4c811e474eda.yaml b/bugs/issue-6c6239e053919531627e706685ed4c811e474eda.yaml
new file mode 100644
index 0000000..00bfc4d
--- /dev/null
+++ b/bugs/issue-6c6239e053919531627e706685ed4c811e474eda.yaml
@@ -0,0 +1,22 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Extend brick connectivity to 2D
+desc: The brick is written for 3D only.  Would be nice to have a 2D version.
+type: :feature
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2010-09-29 16:12:44.591400 Z
+references: []
+
+id: 6c6239e053919531627e706685ed4c811e474eda
+log_events: 
+- - 2010-09-29 16:12:46.423280 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2011-02-11 02:07:55.332646 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - This has been done
diff --git a/bugs/issue-6c92ff2071235b8fd8c279a135bba542f34d03f0.yaml b/bugs/issue-6c92ff2071235b8fd8c279a135bba542f34d03f0.yaml
new file mode 100644
index 0000000..fcb5321
--- /dev/null
+++ b/bugs/issue-6c92ff2071235b8fd8c279a135bba542f34d03f0.yaml
@@ -0,0 +1,39 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make binary load functions accept files written with any mpisize.
+desc: |-
+  Right now the binary connectivity and p4est file formats
+  rely on the mpisize. This should be converted on the fly when reading.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-02-04 17:48:23.153149 Z
+references: []
+
+id: 6c92ff2071235b8fd8c279a135bba542f34d03f0
+log_events: 
+- - 2009-02-04 17:48:24.736963 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2012-08-13 09:18:57.906932 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    The p4est_load_ext function now has an autopartition parameter.
+    If set to true, p4est will load files written with any mpisize.
+    In this case the partition is set to uniform.
+- - 2012-12-09 19:58:35.496342 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition fixed
+  - |-
+    Loading of previous format versions will not be implemented.
+    Features are complete for current binary load/save
+- - 2013-01-15 15:35:33.935213 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition fixed
+  - |-
+    The on-the-fly conversion on reading is a wontfix.
+    Other than that, everything works.
diff --git a/bugs/issue-702339262c6678519fb1edc5de7d67e3af640eae.yaml b/bugs/issue-702339262c6678519fb1edc5de7d67e3af640eae.yaml
new file mode 100644
index 0000000..eefc7d8
--- /dev/null
+++ b/bugs/issue-702339262c6678519fb1edc5de7d67e3af640eae.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Implement brick-shaped configurable connectivity
+desc: |-
+  Create a connectivity structure composed of L x M x N trees
+  in cartesian order. Number the trees in z-order.
+type: :feature
+component: p4est
+release: "0.3"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-22 02:11:49.256229 Z
+references: []
+
+id: 702339262c6678519fb1edc5de7d67e3af640eae
+log_events: 
+- - 2008-10-22 02:11:50.136083 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-07-07 03:14:29.641232 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - Implemented by Toby Isaac.
diff --git a/bugs/issue-717986f9897b50adad9096285c6a86ae007729a1.yaml b/bugs/issue-717986f9897b50adad9096285c6a86ae007729a1.yaml
new file mode 100644
index 0000000..f972c21
--- /dev/null
+++ b/bugs/issue-717986f9897b50adad9096285c6a86ae007729a1.yaml
@@ -0,0 +1,29 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Vertex-free corner connectivity
+desc: |-
+  Currently the corner neighbors are identified using vertices.
+  This does not work for periodic or overlapping vertex coordinates.
+  Use topological relations instead, as in 3D edge connectivity.
+type: :bugfix
+component: p4est
+release: "0.3"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-16 17:31:22.710949 Z
+references: []
+
+id: 717986f9897b50adad9096285c6a86ae007729a1
+log_events: 
+- - 2008-10-16 17:31:25.974658 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-10-16 17:36:34.075966 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - assigned to release 0.3 from unassigned
+  - ""
+- - 2009-03-11 18:47:55.350166 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - This is done. The vertices are only used in VTK now.
diff --git a/bugs/issue-78e0a8c03a44fca337602cf71cc983d2eef93290.yaml b/bugs/issue-78e0a8c03a44fca337602cf71cc983d2eef93290.yaml
new file mode 100644
index 0000000..1d6d542
--- /dev/null
+++ b/bugs/issue-78e0a8c03a44fca337602cf71cc983d2eef93290.yaml
@@ -0,0 +1,37 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Implement load/save functionality for the p4est
+desc: |-
+  Write load and save functions that read or write the p4est state to disk.
+  This should include connectivity type, partition and element data.
+  Make sure that the parallel writes are synchronized if necessary.
+type: :feature
+component: p4est
+release: "0.3"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-16 17:44:00.172599 Z
+references: []
+
+id: 78e0a8c03a44fca337602cf71cc983d2eef93290
+log_events: 
+- - 2008-10-16 17:44:01.388406 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-11-01 00:44:50.597206 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - changed status from unstarted to in_progress
+  - Implemented synchronized file creation and concurrent data write.
+- - 2008-11-01 03:04:58.782661 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - commented
+  - |-
+    Now stores all necessary tree information. Data content still missing
+    as well as optional one-by-one synchronization.
+- - 2008-11-02 05:44:12.103743 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - |-
+    One-by-one synchronization implemented, necessary in all cases.
+    Element data is saved and loaded if so desired.
diff --git a/bugs/issue-79ecc3d40e82827970b29b493f68b3f1b6ec574c.yaml b/bugs/issue-79ecc3d40e82827970b29b493f68b3f1b6ec574c.yaml
new file mode 100644
index 0000000..6e89d82
--- /dev/null
+++ b/bugs/issue-79ecc3d40e82827970b29b493f68b3f1b6ec574c.yaml
@@ -0,0 +1,39 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: read-safe file write routines
+desc: Should the files created by the _save routines be readable by all processes immediately?  With parallel I/O, we need to be sure that the writing has complete before any reading begins, maybe with an MPI_Barrier at the end of _save routines?
+type: :task
+component: p4est
+release: 
+reporter: Tobin Isaac <tisaac at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2014-01-27 14:44:25.946743 Z
+references: []
+
+id: 79ecc3d40e82827970b29b493f68b3f1b6ec574c
+log_events: 
+- - 2014-01-27 14:44:29.058674 Z
+  - Tobin Isaac <tisaac at ices.utexas.edu>
+  - created
+  - ""
+- - 2014-01-28 17:11:05.821708 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    We could document that MPI_File_Close is called.
+    The user is then referred to the MPI standard and can take their own actions.
+    I think a barrier would not be transparent and rarely needed.
+    Propose to keep as is with added documentation.
+- - 2014-02-01 16:23:10.905239 Z
+  - Tobin Isaac <tisaac at ices.utexas.edu>
+  - commented
+  - |-
+    if P4EST_MPIIO_WRITE is not defined, processes write in serial, and each exits
+    as soon as it has written its section.  Comment added to documentation about
+    this issue.
+- - 2014-02-08 16:44:06.149912 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    For non-MPI I/O configuration, we might indeed add a barrier or broadcast.
+    Also thinking to enable MPI I/O by default and run a configure test for it.
diff --git a/bugs/issue-7a606717f830cd9686ebaa301e13ee80dba3be1c.yaml b/bugs/issue-7a606717f830cd9686ebaa301e13ee80dba3be1c.yaml
new file mode 100644
index 0000000..f129bbe
--- /dev/null
+++ b/bugs/issue-7a606717f830cd9686ebaa301e13ee80dba3be1c.yaml
@@ -0,0 +1,22 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Fix p4est_comm_neighborhood_owned
+desc: |-
+  The full_tree variables only cover tree face neighbors.
+  For some connectivities, there may only be tree edge or corner neighbors.
+  In this case, p4est_comm_neighborhood_owned may return a false positive.
+  We should also document that it may return a false negative.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-04-02 11:59:52.880003 Z
+references: []
+
+id: 7a606717f830cd9686ebaa301e13ee80dba3be1c
+log_events: 
+- - 2014-04-02 11:59:53.487977 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
diff --git a/bugs/issue-7ab3610df158776fa1572b84e130635772281b35.yaml b/bugs/issue-7ab3610df158776fa1572b84e130635772281b35.yaml
new file mode 100644
index 0000000..5632817
--- /dev/null
+++ b/bugs/issue-7ab3610df158776fa1572b84e130635772281b35.yaml
@@ -0,0 +1,31 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make vertices optional in p4est core functionality
+desc: |-
+  Once {issue 717986f9897b50adad9096285c6a86ae007729a1} is fixed, vertices are only needed for VTK output
+  or other geometry processing.  So it should be allowed to have
+  no vertices at all, especially in the load/save functions.
+type: :bugfix
+component: p4est
+release: "0.3"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-11-11 23:29:56.418996 Z
+references: []
+
+id: 7ab3610df158776fa1572b84e130635772281b35
+log_events: 
+- - 2008-11-11 23:29:57.282914 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-03-19 19:53:17.892782 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - commented
+  - This is good in 3D. Need to do it all over for 2D.
+- - 2009-10-12 23:42:32.660868 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - |-
+    This was done in the 2D z-order conversion.
+    The loadsave test was updated to test both 2D and 3D without vertices.
diff --git a/bugs/issue-84a74da0a3a4f357666853d81ce8280d22d3d6e1.yaml b/bugs/issue-84a74da0a3a4f357666853d81ce8280d22d3d6e1.yaml
new file mode 100644
index 0000000..2c9704e
--- /dev/null
+++ b/bugs/issue-84a74da0a3a4f357666853d81ce8280d22d3d6e1.yaml
@@ -0,0 +1,31 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make corner ghosts work irrespective of corner balance
+desc: |-
+  The logic for the ghost layer can be changed so it checks ownership
+  of the smallest possible corner neighbor. This would guarantee
+  that build_ghost_layer (FULL) is successful for balance (EDGE) only.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :reorg
+creation_time: 2008-10-20 19:23:12.081863 Z
+references: []
+
+id: 84a74da0a3a4f357666853d81ce8280d22d3d6e1
+log_events: 
+- - 2008-10-20 19:23:13.649694 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2010-01-22 17:45:44.798768 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 0.3
+  - ""
+- - 2010-07-07 15:34:06.051955 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition reorg
+  - |-
+    This is superseded by the new ghost layer implementation
+    that does not require any balance condition anymore.
diff --git a/bugs/issue-8bb5112f86923515c14a987340d571ade618963a.yaml b/bugs/issue-8bb5112f86923515c14a987340d571ade618963a.yaml
new file mode 100644
index 0000000..dab4ba8
--- /dev/null
+++ b/bugs/issue-8bb5112f86923515c14a987340d571ade618963a.yaml
@@ -0,0 +1,28 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make the twocubes example periodic
+desc: |-
+  The twocubes example can make periodic when the corner connectivity
+  has been fixed to allow this situation.
+type: :task
+component: p4est
+release: "0.3"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-16 17:34:41.359987 Z
+references: []
+
+id: 8bb5112f86923515c14a987340d571ade618963a
+log_events: 
+- - 2008-10-16 17:34:46.895639 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-10-16 17:36:39.931776 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - assigned to release 0.3 from unassigned
+  - ""
+- - 2009-10-13 17:55:14.421307 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - Resolved by adding a twowrap connectivity in 3D which is periodic.
diff --git a/bugs/issue-927e4b037e69d0b970faa9e882fde8519f9b21b7.yaml b/bugs/issue-927e4b037e69d0b970faa9e882fde8519f9b21b7.yaml
new file mode 100644
index 0000000..c52b36c
--- /dev/null
+++ b/bugs/issue-927e4b037e69d0b970faa9e882fde8519f9b21b7.yaml
@@ -0,0 +1,25 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: "Replace p4est_to_p8est.h with P48EST ## construction"
+desc: |-
+  Currently p4est_to_p8est.h redefines every p4est function, data type, etc.
+  This can lead to unexpected results if not used correctly.
+  Using macro expansion and concatenation could eliminate wrong usage.
+type: :task
+component: p4est
+release: "1.4"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :wontfix
+creation_time: 2008-10-16 19:08:09.693280 Z
+references: []
+
+id: 927e4b037e69d0b970faa9e882fde8519f9b21b7
+log_events: 
+- - 2008-10-16 19:08:10.957076 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2010-07-05 20:13:38.686646 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition wontfix
+  - It works for us.  People can write wrappers if they want.
diff --git a/bugs/issue-92dbc3c718f4517055012c7206a9b2ac8171298b.yaml b/bugs/issue-92dbc3c718f4517055012c7206a9b2ac8171298b.yaml
new file mode 100644
index 0000000..62f6f07
--- /dev/null
+++ b/bugs/issue-92dbc3c718f4517055012c7206a9b2ac8171298b.yaml
@@ -0,0 +1,22 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Distribute connectivity in parallel
+desc: |-
+  Design a connectivity format where tree storage is distributed.
+  Will need to include sharing a layer of ghost octrees.
+  Will need repartitioning of the octrees from p4est_partition.
+  This should remove the current limit of about 10**5--10**6 octrees.
+type: :bugfix
+component: p4est
+release: "1.5"
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2013-05-11 15:43:51.216569 Z
+references: []
+
+id: 92dbc3c718f4517055012c7206a9b2ac8171298b
+log_events: 
+- - 2013-05-11 15:44:06.336136 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - Release 1.5 should be fine for zillions of octrees.
diff --git a/bugs/issue-a1c658fed16cb6905d1f2a36f2f6d31129cfd626.yaml b/bugs/issue-a1c658fed16cb6905d1f2a36f2f6d31129cfd626.yaml
new file mode 100644
index 0000000..eb46b8c
--- /dev/null
+++ b/bugs/issue-a1c658fed16cb6905d1f2a36f2f6d31129cfd626.yaml
@@ -0,0 +1,33 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Introduce is_strict_ancestor
+desc: |-
+  Rename the current is_ancestor routine to is_strict_ancestor.
+  Introduce an is_ancestor routine that allows for equality.
+  This means that the meaning of the is_ancestor routine changes.
+  Go through all of p4est to update the code accordingly.
+  If we miss any of these occurrences that could be very bad.
+type: :bugfix
+component: p4est
+release: "1.4"
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2013-05-11 15:38:58.608756 Z
+references: []
+
+id: a1c658fed16cb6905d1f2a36f2f6d31129cfd626
+log_events: 
+- - 2013-05-11 15:39:04.906159 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
+- - 2014-03-18 10:15:26.154771 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - assigned to release 1.4 from unassigned
+  - We should resolve this issue one way or another.
+- - 2014-05-02 14:54:50.503541 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    For maximum safety, what about switching to the new function names
+    p4est_is_equal_or_ancestor, p4est_is_unequal/strict_ancestor?
diff --git a/bugs/issue-a456c21b12a649522ff76e7e34120e06609f3d36.yaml b/bugs/issue-a456c21b12a649522ff76e7e34120e06609f3d36.yaml
new file mode 100644
index 0000000..b3bdc22
--- /dev/null
+++ b/bugs/issue-a456c21b12a649522ff76e7e34120e06609f3d36.yaml
@@ -0,0 +1,36 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make ghost_ext/iterate_ext proper API functions
+desc: |-
+  The ghost and iterate versions in p4est_extended.h should be official
+  p4est API functions and moved/renamed into p4est_{ghost,iterate}.h,
+  respectively.
+  If we have several versions we can easily use postfixes such as
+  p4est_ghost_new_with_feature_something.
+type: :bugfix
+component: p4est
+release: "1.4"
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-03-18 10:10:42.506995 Z
+references: []
+
+id: a456c21b12a649522ff76e7e34120e06609f3d36
+log_events: 
+- - 2014-03-18 10:10:43.334029 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
+- - 2014-05-09 09:01:35.286084 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    The ghost API seems fine.
+    balance_subtree(_ext) should be just one function not in p4est_extended.h.
+    balance_ext needs to be documented.  Other than that, the file seems fine.
+- - 2014-05-14 09:23:32.138605 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    balance_ext and balance_subtree_ext can stay in p4est_extended.
+    They just need to be properly doxygenated.
diff --git a/bugs/issue-a45b59075f1c1830366c414703090e6ed4875a45.yaml b/bugs/issue-a45b59075f1c1830366c414703090e6ed4875a45.yaml
new file mode 100644
index 0000000..ad5af7a
--- /dev/null
+++ b/bugs/issue-a45b59075f1c1830366c414703090e6ed4875a45.yaml
@@ -0,0 +1,32 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Implement load/save functionality for the connectivity
+desc: |-
+  The connectivity structure is identical on all cores and needs only
+  to be saved once.
+type: :feature
+component: p4est
+release: "0.3"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-16 23:13:44.469521 Z
+references: []
+
+id: a45b59075f1c1830366c414703090e6ed4875a45
+log_events: 
+- - 2008-10-16 23:13:59.620707 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-10-16 23:14:53.378858 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - changed status from unstarted to in_progress
+  - ""
+- - 2008-10-17 00:56:44.876361 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - commented
+  - Implemented 2D connectivity save/load/is_equal and added a test.
+- - 2008-10-17 01:44:45.383637 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - Connectivity load/save seems working for 2D and 3D.
diff --git a/bugs/issue-a54552a06eb8c1c9130c969909ef566c84b217ef.yaml b/bugs/issue-a54552a06eb8c1c9130c969909ef566c84b217ef.yaml
new file mode 100644
index 0000000..fc7d109
--- /dev/null
+++ b/bugs/issue-a54552a06eb8c1c9130c969909ef566c84b217ef.yaml
@@ -0,0 +1,31 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Optimize ghost layer
+desc: |-
+  A few things can be changed in the ghost layer.
+  1. API: return a structure that contains offsets for trees and procs.
+  2. Test if changing two-round comm to one-round mpi_probe makes it faster.
+  3. Test if waitsome or waitany instead of waitall makes it faster.
+  4. Limit binary searches to an array view that only contains one tree.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-08-12 17:05:21.842088 Z
+references: []
+
+id: a54552a06eb8c1c9130c969909ef566c84b217ef
+log_events: 
+- - 2009-08-12 17:05:24.865879 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-10-13 18:05:34.748963 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - changed status from unstarted to in_progress
+  - Taks 1. and 4. have been implemented.
+- - 2010-09-29 15:30:29.570672 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - While 2 and 3 are still open, other optimizations have been used.
diff --git a/bugs/issue-acb1ebc87d79bd1744490f330b0e0abdbe7944aa.yaml b/bugs/issue-acb1ebc87d79bd1744490f330b0e0abdbe7944aa.yaml
new file mode 100644
index 0000000..a13694b
--- /dev/null
+++ b/bugs/issue-acb1ebc87d79bd1744490f330b0e0abdbe7944aa.yaml
@@ -0,0 +1,22 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Write VTK output into one partition-independent .vtu file
+desc: |-
+  Currently each processor writes one .vtu file.  This is not practicable
+  for large numbers of processors.  We can instead use MPI I/O to create
+  only one (or a smaller number of) .vtu files that appear to have been
+  written by one (or few) processes.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-03-27 09:56:21.110167 Z
+references: []
+
+id: acb1ebc87d79bd1744490f330b0e0abdbe7944aa
+log_events: 
+- - 2014-03-27 09:56:21.702038 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
diff --git a/bugs/issue-b295c3069e005f7679d9fca88f890bd1a54f2535.yaml b/bugs/issue-b295c3069e005f7679d9fca88f890bd1a54f2535.yaml
new file mode 100644
index 0000000..bfe76bd
--- /dev/null
+++ b/bugs/issue-b295c3069e005f7679d9fca88f890bd1a54f2535.yaml
@@ -0,0 +1,22 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Simplify p4est_comm_is_owner and p4est_comm_find_owner
+desc: |-
+  In p4est_comm_neighborhood_owned, it would be sufficient to call
+  p4est_comm_is_owner instead of the more expensife p4est_comm_find_owner.
+  Both of these functions can be simplified by using
+  p4est_quadrant_first_descendant and p4est_quadrant_compare_piggy.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-04-02 11:44:55.908031 Z
+references: []
+
+id: b295c3069e005f7679d9fca88f890bd1a54f2535
+log_events: 
+- - 2014-04-02 11:44:56.419998 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
diff --git a/bugs/issue-bd78e83b886f8721a5acd9bbb43e9c6a52d0c972.yaml b/bugs/issue-bd78e83b886f8721a5acd9bbb43e9c6a52d0c972.yaml
new file mode 100644
index 0000000..9466af8
--- /dev/null
+++ b/bugs/issue-bd78e83b886f8721a5acd9bbb43e9c6a52d0c972.yaml
@@ -0,0 +1,32 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Deal with p4est counters modified by balance_subtree
+desc: |-
+  Make sure that the p4est counters are correct throughout the helper
+  functions in p4est_algorithms.
+type: :bugfix
+component: p4est
+release: "1.4"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2008-10-16 17:41:05.586582 Z
+references: []
+
+id: bd78e83b886f8721a5acd9bbb43e9c6a52d0c972
+log_events: 
+- - 2008-10-16 17:41:06.578555 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2010-01-22 17:45:41.550867 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 0.3
+  - ""
+- - 2013-01-15 15:38:08.365125 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - We probably won't need that.  We could remove the per-level counters entirely.
+- - 2014-03-18 10:16:04.711515 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - assigned to release 1.4 from unassigned
+  - We should think about how we want it and resolve the issue.
diff --git a/bugs/issue-bd7f52770dfd69f68b648f6388fb538ff335ef4c.yaml b/bugs/issue-bd7f52770dfd69f68b648f6388fb538ff335ef4c.yaml
new file mode 100644
index 0000000..593491e
--- /dev/null
+++ b/bugs/issue-bd7f52770dfd69f68b648f6388fb538ff335ef4c.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Add num_nonempty_processors member variable to p4est?
+desc: |-
+  This number is currently only available by iterating through the
+  global_first_quadrant array, which is somewhat costly.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :closed
+disposition: :wontfix
+creation_time: 2014-03-27 10:26:11.725022 Z
+references: []
+
+id: bd7f52770dfd69f68b648f6388fb538ff335ef4c
+log_events: 
+- - 2014-03-27 10:26:12.140968 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
+- - 2014-05-14 09:25:01.136575 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition wontfix
+  - This would add same redundancy, let's not do this now.
diff --git a/bugs/issue-bf1b9e01cc043735905673a2e7c1a25e5ed7a58f.yaml b/bugs/issue-bf1b9e01cc043735905673a2e7c1a25e5ed7a58f.yaml
new file mode 100644
index 0000000..e030b9b
--- /dev/null
+++ b/bugs/issue-bf1b9e01cc043735905673a2e7c1a25e5ed7a58f.yaml
@@ -0,0 +1,22 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Convert 2D connectivity from right hand rule to z-order
+desc: ""
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-02-04 17:24:25.203066 Z
+references: []
+
+id: bf1b9e01cc043735905673a2e7c1a25e5ed7a58f
+log_events: 
+- - 2009-02-04 17:24:26.890839 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-10-13 18:03:09.220601 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - This has been done.
diff --git a/bugs/issue-cad051b8c32fd80d9c39303096e34988b526d0b0.yaml b/bugs/issue-cad051b8c32fd80d9c39303096e34988b526d0b0.yaml
new file mode 100644
index 0000000..2e9c42a
--- /dev/null
+++ b/bugs/issue-cad051b8c32fd80d9c39303096e34988b526d0b0.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make p6est_layer_init/free_data extern
+desc: |-
+  The two static non-inline functions in p6est.h cause a warning.
+  Can these functions be made extern and defined in p6est.c?
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-02-18 20:36:38.965551 Z
+references: []
+
+id: cad051b8c32fd80d9c39303096e34988b526d0b0
+log_events: 
+- - 2014-02-18 20:36:40.432498 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
+- - 2014-02-19 15:55:07.465952 Z
+  - Tobin Isaac <tisaac at ices.utexas.edu>
+  - commented
+  - I made them static inline instead.
diff --git a/bugs/issue-cfdac5bda3344be1e1cdb6a7f70ceac8a1257cf8.yaml b/bugs/issue-cfdac5bda3344be1e1cdb6a7f70ceac8a1257cf8.yaml
new file mode 100644
index 0000000..98bdc11
--- /dev/null
+++ b/bugs/issue-cfdac5bda3344be1e1cdb6a7f70ceac8a1257cf8.yaml
@@ -0,0 +1,43 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make edge/face only balance optimal and configurable
+desc: |-
+  Optimal refers to inter-tree balance where currently full face, edge and
+  corner balance is performed always.
+  Configurable means to make edge and/or corner balance optional.
+type: :feature
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-16 17:40:03.876875 Z
+references: []
+
+id: cfdac5bda3344be1e1cdb6a7f70ceac8a1257cf8
+log_events: 
+- - 2008-10-16 17:40:07.204566 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-10-16 18:39:39.064627 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - changed status from unstarted to in_progress
+  - Implementing flags for the balance and is_balanced API functions.
+- - 2008-10-16 20:56:45.657798 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - commented
+  - The flags are in place now but without any effect yet.
+- - 2008-10-16 23:04:52.264246 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - changed status from in_progress to paused
+  - |-
+    Balance, ghost layer and balance check are now configurable.
+    Inter-tree balance always does edges and corners, may not be necessary.
+- - 2010-01-22 17:46:10.261844 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 0.3
+  - ""
+- - 2013-01-15 20:35:43.643167 Z
+  - Tobin Isaac <tisaac at wildspitze.ices.utexas.edu>
+  - closed with disposition fixed
+  - New balance algorithm is optimal.
diff --git a/bugs/issue-d0e99b9f6d2204e88c8e00a2c0812ae65c2910d5.yaml b/bugs/issue-d0e99b9f6d2204e88c8e00a2c0812ae65c2910d5.yaml
new file mode 100644
index 0000000..35d2734
--- /dev/null
+++ b/bugs/issue-d0e99b9f6d2204e88c8e00a2c0812ae65c2910d5.yaml
@@ -0,0 +1,21 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Implement EtoV-based connectivity lua file format
+desc: |-
+  This file format should provide EtoV information,
+  vertex coordinates, piecewise analytical geometry transformations,
+  tags for elements, element faces, element edges and element corners.
+type: :feature
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2009-02-04 17:37:14.952291 Z
+references: []
+
+id: d0e99b9f6d2204e88c8e00a2c0812ae65c2910d5
+log_events: 
+- - 2009-02-04 17:37:27.671899 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
diff --git a/bugs/issue-d201dca19725faf673774d97bd0bf41ff6a73da4.yaml b/bugs/issue-d201dca19725faf673774d97bd0bf41ff6a73da4.yaml
new file mode 100644
index 0000000..cfd20a0
--- /dev/null
+++ b/bugs/issue-d201dca19725faf673774d97bd0bf41ff6a73da4.yaml
@@ -0,0 +1,27 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: p4est_ghost
+desc: |-
+  Mirror fronts seem to be set always even though docs say they are NULL.
+  My preference is to keep them NULL until ghost_expand is called.
+  Also, ghost_memory_used does not reflect all members yet.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-05-09 09:37:31.585055 Z
+references: []
+
+id: d201dca19725faf673774d97bd0bf41ff6a73da4
+log_events: 
+- - 2014-05-09 09:37:32.320829 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
+- - 2014-05-14 09:26:40.168944 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    The doxygen might be slightly improved on ghost in general.
+    p4est_ghost_is_valid needs to test the fronts too.
diff --git a/bugs/issue-d5068a39cb5f38a40f234e9552119fbf4a2a791d.yaml b/bugs/issue-d5068a39cb5f38a40f234e9552119fbf4a2a791d.yaml
new file mode 100644
index 0000000..f8d633f
--- /dev/null
+++ b/bugs/issue-d5068a39cb5f38a40f234e9552119fbf4a2a791d.yaml
@@ -0,0 +1,28 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make vtk header fields configurable
+desc: |-
+  Both the 2D and 3D vtk output routines should accept parameters
+  for selectively writing the tree id, mpi rank, and a wrapped mpi rank.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-30 22:03:51.715030 Z
+references: []
+
+id: d5068a39cb5f38a40f234e9552119fbf4a2a791d
+log_events: 
+- - 2008-10-30 22:03:53.186897 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2010-01-22 17:45:58.726399 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 0.3
+  - ""
+- - 2014-05-14 09:24:02.421839 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition fixed
+  - This is done.
diff --git a/bugs/issue-d93672ccf1d9e0283d94c6952d2f7d3b0530de27.yaml b/bugs/issue-d93672ccf1d9e0283d94c6952d2f7d3b0530de27.yaml
new file mode 100644
index 0000000..367af6b
--- /dev/null
+++ b/bugs/issue-d93672ccf1d9e0283d94c6952d2f7d3b0530de27.yaml
@@ -0,0 +1,30 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Write vector versions of qcoord_to_vertex and children.
+desc: |-
+  Provide a function p4est_qcoord_to_vertexv that processes any number
+  of quadrant coordinate triples.
+  Provide p4est_quadrant_childrenpv analogously to p4est_is_family_pv.
+type: :feature
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2010-04-06 18:47:57.384030 Z
+references: []
+
+id: d93672ccf1d9e0283d94c6952d2f7d3b0530de27
+log_events: 
+- - 2010-04-06 18:47:58.375779 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2011-02-11 02:34:00.953462 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - |-
+    A vector version of qcoord_to_vertex is not necessary.
+    It can be written when needed by wrapping the one-at-a-time function.
+    Not clear how much optimization can possibly be done more intrusively.
+    
+    p4est_childrenpv has been added.
diff --git a/bugs/issue-e05575fcbdce35574cac3d09b8688ec77c791db0.yaml b/bugs/issue-e05575fcbdce35574cac3d09b8688ec77c791db0.yaml
new file mode 100644
index 0000000..2d7cb9e
--- /dev/null
+++ b/bugs/issue-e05575fcbdce35574cac3d09b8688ec77c791db0.yaml
@@ -0,0 +1,25 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Use transformations from lua connectivity file in VTK output
+desc: |-
+  The VTK output can make use of the vertex coordinates and transformations
+  that are given with the EtoV information to render the domain
+  realistically.
+type: :feature
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :reorg
+creation_time: 2009-02-04 17:39:44.419216 Z
+references: []
+
+id: e05575fcbdce35574cac3d09b8688ec77c791db0
+log_events: 
+- - 2009-02-04 17:39:55.546658 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2014-05-18 09:31:14.434061 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition reorg
+  - With the current p4est_geometry interface, this can be implemented as needed.
diff --git a/bugs/issue-e12f2621a32bbffc2a9704a189e8229d3dbaf412.yaml b/bugs/issue-e12f2621a32bbffc2a9704a189e8229d3dbaf412.yaml
new file mode 100644
index 0000000..d4f2c76
--- /dev/null
+++ b/bugs/issue-e12f2621a32bbffc2a9704a189e8229d3dbaf412.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Only store local trees
+desc: |-
+  Currently p4est->trees stores all trees, even empty ones.
+  This is a waste of memory with many trees.  Only store non-empty ones.
+type: :bugfix
+component: p4est
+release: "1.5"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2011-07-31 23:15:57.768188 Z
+references: []
+
+id: e12f2621a32bbffc2a9704a189e8229d3dbaf412
+log_events: 
+- - 2011-07-31 23:15:58.956090 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2013-05-11 15:41:40.120676 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - assigned to release 1.5 from unassigned
+  - Release 1.5 should contain major improvements for many trees
diff --git a/bugs/issue-e3fb3ea10bbe0196c325c92744bfc3eab28c98da.yaml b/bugs/issue-e3fb3ea10bbe0196c325c92744bfc3eab28c98da.yaml
new file mode 100644
index 0000000..14f31c5
--- /dev/null
+++ b/bugs/issue-e3fb3ea10bbe0196c325c92744bfc3eab28c98da.yaml
@@ -0,0 +1,28 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Check potentially overlapping ghost layer quadrants
+desc: |-
+  Check how quadrants across tree corners or edges are handled.
+  How is the overlap encoded? What implications does this have
+  for the p4est algorithms?
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-02-11 22:39:28.956924 Z
+references: []
+
+id: e3fb3ea10bbe0196c325c92744bfc3eab28c98da
+log_events: 
+- - 2009-02-11 22:39:29.604725 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-08-12 17:03:41.621572 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - |-
+    There was no problem.  Ghosts are ordered by tree first
+    so when they are from different trees they are never searched for
+    in the same call (all searches are only within one tree).
diff --git a/bugs/issue-e83652f1ee3e88a3ee7917ea288139cb7245f937.yaml b/bugs/issue-e83652f1ee3e88a3ee7917ea288139cb7245f937.yaml
new file mode 100644
index 0000000..f429155
--- /dev/null
+++ b/bugs/issue-e83652f1ee3e88a3ee7917ea288139cb7245f937.yaml
@@ -0,0 +1,36 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make inter-tree face-only and edge-only balance work
+desc: |-
+  While face and edge only balance works correctly inside each tree,
+  across trees we do full balance always. This can lead to situations
+  where element lengths before and after balance differ in a factor of 4.
+  This in turn must not happen for e.g. rhea and mangll which expect 2:1.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-30 23:27:32.641358 Z
+references: []
+
+id: e83652f1ee3e88a3ee7917ea288139cb7245f937
+log_events: 
+- - 2008-10-30 23:27:33.377241 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-10-31 02:45:09.385321 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - commented
+  - |-
+    The inter-tree balance can be relaxed to face or edge only
+    once the edge and corner minimum connectivity is ensured.
+- - 2010-01-22 17:46:01.206176 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 0.3
+  - ""
+- - 2013-01-15 20:37:53.333187 Z
+  - Tobin Isaac <tisaac at wildspitze.ices.utexas.edu>
+  - closed with disposition fixed
+  - New balance algorithm correctly performed face/edge-only intertree balance
diff --git a/bugs/issue-ea05cc814c6b8c0c5ffb97c518cedc9bf7d55d10.yaml b/bugs/issue-ea05cc814c6b8c0c5ffb97c518cedc9bf7d55d10.yaml
new file mode 100644
index 0000000..ade50a9
--- /dev/null
+++ b/bugs/issue-ea05cc814c6b8c0c5ffb97c518cedc9bf7d55d10.yaml
@@ -0,0 +1,26 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make octant creation callback know where it's coming from
+desc: |-
+  On refinement, the creation callback should know the parent to be replaced.
+  On coarsening, it should know the children to be replaced.
+type: :feature
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-03-17 18:58:29.056069 Z
+references: []
+
+id: ea05cc814c6b8c0c5ffb97c518cedc9bf7d55d10
+log_events: 
+- - 2009-03-17 18:58:30.463924 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2013-01-15 15:36:35.185107 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition fixed
+  - |-
+    This is now implemented in refine_ext and coarsen_ext.
+    In addition, balance_ext is aware of this information as well.
diff --git a/bugs/issue-ee03a7e2b63af3a9124e968631c1c0a1fc67d09d.yaml b/bugs/issue-ee03a7e2b63af3a9124e968631c1c0a1fc67d09d.yaml
new file mode 100644
index 0000000..8d69fda
--- /dev/null
+++ b/bugs/issue-ee03a7e2b63af3a9124e968631c1c0a1fc67d09d.yaml
@@ -0,0 +1,20 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Remove all dependencies on sc_ranges
+desc: |-
+  The sc/sc_ranges.{c,h} code is no longer necessary.
+  Use sc_notify throughout and remove obsolete code.
+type: :bugfix
+component: p4est
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-02-08 17:37:13.973371 Z
+references: []
+
+id: ee03a7e2b63af3a9124e968631c1c0a1fc67d09d
+log_events: 
+- - 2014-02-08 17:37:15.681968 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
diff --git a/bugs/issue-ee31c9c10ebd498fb6756ec3d2f0781602c86191.yaml b/bugs/issue-ee31c9c10ebd498fb6756ec3d2f0781602c86191.yaml
new file mode 100644
index 0000000..5536cc4
--- /dev/null
+++ b/bugs/issue-ee31c9c10ebd498fb6756ec3d2f0781602c86191.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Fix documentation of ghost layer
+desc: |-
+  The documentation of p4est_build_ghost_layer states that piggy1 is used.
+  In the code it looks more like piggy3.  Make this consistent.
+type: :bugfix
+component: p4est
+release: "0.3"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-02-25 20:11:23.975917 Z
+references: []
+
+id: ee31c9c10ebd498fb6756ec3d2f0781602c86191
+log_events: 
+- - 2009-02-25 20:11:24.815744 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-08-12 17:09:53.033046 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - This has been fixed.
diff --git a/bugs/issue-f92033d821dcf3de6c4e863ea163100ee0f7a069.yaml b/bugs/issue-f92033d821dcf3de6c4e863ea163100ee0f7a069.yaml
new file mode 100644
index 0000000..68644ed
--- /dev/null
+++ b/bugs/issue-f92033d821dcf3de6c4e863ea163100ee0f7a069.yaml
@@ -0,0 +1,25 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Implement human-readable machine-independent load and save
+desc: |-
+  Based on JSON or YAML, write text-based load/save code.
+  Possibly add a zlib compression option.
+  This makes the p4est and connectivity files machine independent.
+type: :feature
+component: p4est
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2008-10-30 17:04:56.578890 Z
+references: []
+
+id: f92033d821dcf3de6c4e863ea163100ee0f7a069
+log_events: 
+- - 2008-10-30 17:04:57.634788 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2010-09-29 15:33:52.812363 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 1.5
+  - ""
diff --git a/bugs/project.yaml b/bugs/project.yaml
new file mode 100644
index 0000000..744a8b9
--- /dev/null
+++ b/bugs/project.yaml
@@ -0,0 +1,38 @@
+--- !ditz.rubyforge.org,2008-03-06/project 
+name: p4est
+version: "1.1"
+components: 
+- !ditz.rubyforge.org,2008-03-06/component 
+  name: p4est
+releases: 
+- !ditz.rubyforge.org,2008-03-06/release 
+  name: "0.3"
+  status: :released
+  release_time: 2010-07-07 16:06:55.540168 Z
+  log_events: 
+  - - 2008-10-16 17:36:04.252955 Z
+    - Carsten Burstedde <carsten at ices.utexas.edu>
+    - created
+    - Release 0.3 should have full 2D and 3D functionality.
+  - - 2010-07-07 16:06:55.540184 Z
+    - Carsten Burstedde <carsten at ices.utexas.edu>
+    - released
+    - This has been done on 2010-01-22.
+- !ditz.rubyforge.org,2008-03-06/release 
+  name: "1.4"
+  status: :unreleased
+  release_time: 
+  log_events: 
+  - - 2008-10-16 19:05:39.234410 Z
+    - Carsten Burstedde <carsten at ices.utexas.edu>
+    - created
+    - Implement and test all code necessary for inclusion in Deal.II.
+- !ditz.rubyforge.org,2008-03-06/release 
+  name: "1.5"
+  status: :unreleased
+  release_time: 
+  log_events: 
+  - - 2008-10-30 17:02:26.232138 Z
+    - Carsten Burstedde <carsten at ices.utexas.edu>
+    - created
+    - This release contains extra features added after public release.
diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen
new file mode 100755
index 0000000..b6c9d41
--- /dev/null
+++ b/build-aux/git-version-gen
@@ -0,0 +1,162 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2008-04-08.07
+
+# Copyright (C) 2007-2008 Free Software Foundation
+#
+# 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, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+#   produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+#   presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+#   a checked-out repository.  Created with contents that were learned at
+#   the last time autoconf was run, and used by git-version-gen.  Must not
+#   be present in either $(srcdir) or $(builddir) for git-version-gen to
+#   give accurate answers during normal development with a checked out tree,
+#   but must be present in a tarball when there is no version control system.
+#   Therefore, it cannot be used in any dependencies.  GNUmakefile has
+#   hooks to force a reconfigure at distribution time to get the value
+#   correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+#   tarball.  Usable in dependencies, particularly for files that don't
+#   want to depend on config.h but do want to track version changes.
+#   Delete this file prior to any autoconf run where you want to rebuild
+#   files to pick up a version string change; and leave it stale to
+#   minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+#         m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+#         [bug-project at example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+#	echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+#	echo $(VERSION) > $(distdir)/.tarball-version
+
+# Copyright (C) 2014 Carsten Burstedde
+#
+# With the new style of git submodules, .git may be a file: added test -f .git
+
+case $# in
+  1) ;;
+  *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
+esac
+
+srcdir=`dirname $1`
+tarball_version_file=`basename $1`
+nl='
+'
+
+# Change directory into the srcdir.  This should allow
+# for out of source make dist.
+cd $srcdir
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+  v=`cat $tarball_version_file` || exit 1
+  case $v in
+    *$nl*) v= ;; # reject multi-line output
+    [0-9]*) ;;
+    *) v= ;;
+  esac
+  test -z "$v" \
+  && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+
+if test -n "$v"
+then
+  : # use $v
+elif { test -d .git || test -f .git ; } \
+  && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
+  || git describe --abbrev=4 HEAD 2>/dev/null` \
+  && case $v in
+  v[0-9]*) ;;
+  *) (exit 1) ;;
+esac
+then
+  # Is this a new git that lists number of commits since the last
+  # tag or the previous older version that did not?
+  #   Newer: v6.10-77-g0f8faeb
+  #   Older: v6.10-g0f8faeb
+  case $v in
+    *-*-*) : git describe is okay three part flavor ;;
+    *-*)
+    : git describe is older two part flavor
+    # Recreate the number of commits and rewrite such that the
+    # result is the same as if we were using the newer version
+    # of git describe.
+    vtag=`echo "$v" | sed 's/-.*//'`
+    numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+    v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+    ;;
+  esac
+
+    # Change the first '-' to a '.', so version-comparing tools work properly.
+    # Remove the "g" in git describe's output string, to save a byte.
+    v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+else
+    v=UNKNOWN
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git status > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+  '') ;;
+  *) # Append the suffix only if there isn't one already.
+  case $v in
+    *-dirty) ;;
+    *) v="$v-dirty" ;;
+  esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d '\012'
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/build-aux/git2cl b/build-aux/git2cl
new file mode 100755
index 0000000..aa1e8c1
--- /dev/null
+++ b/build-aux/git2cl
@@ -0,0 +1,308 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2007 Simon Josefsson.
+#
+# The functions mywrap, last_line_len, wrap_log_entry are derived from
+# the cvs2cl tool, see <http://www.red-bean.com/cvs2cl/>:
+# Copyright (C) 2001,2002,2003,2004 Martyn J. Pearce <fluffy at cpan.org>
+# Copyright (C) 1999 Karl Fogel <kfogel at red-bean.com>
+#
+# git2cl is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# git2cl 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 git2cl; see the file COPYING.  If not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+use strict;
+use Date::Parse qw(strptime);
+use POSIX qw(strftime);
+use Text::Wrap qw(wrap);
+
+use constant EMPTY_LOG_MESSAGE => '*** empty log message ***';
+
+sub mywrap {
+    my ($indent1, $indent2, @text) = @_;
+    # If incoming text looks preformatted, don't get clever
+    my $text = Text::Wrap::wrap($indent1, $indent2, @text);
+    if ( grep /^\s+/m, @text ) {
+	return $text;
+    }
+    my @lines = split /\n/, $text;
+    $indent2 =~ s!^((?: {8})+)!"\t" x (length($1)/8)!e;
+    $lines[0] =~ s/^$indent1\s+/$indent1/;
+    s/^$indent2\s+/$indent2/
+	for @lines[1..$#lines];
+    my $newtext = join "\n", @lines;
+    $newtext .= "\n"
+	if substr($text, -1) eq "\n";
+    return $newtext;
+}
+
+sub last_line_len {
+    my $files_list = shift;
+    my @lines = split (/\n/, $files_list);
+    my $last_line = pop (@lines);
+    return length ($last_line);
+}
+
+# A custom wrap function, sensitive to some common constructs used in
+# log entries.
+sub wrap_log_entry {
+    my $text = shift;                  # The text to wrap.
+    my $left_pad_str = shift;          # String to pad with on the left.
+
+    # These do NOT take left_pad_str into account:
+    my $length_remaining = shift;      # Amount left on current line.
+    my $max_line_length  = shift;      # Amount left for a blank line.
+
+    my $wrapped_text = '';             # The accumulating wrapped entry.
+    my $user_indent = '';              # Inherited user_indent from prev line.
+
+    my $first_time = 1;                # First iteration of the loop?
+    my $suppress_line_start_match = 0; # Set to disable line start checks.
+
+    my @lines = split (/\n/, $text);
+    while (@lines)   # Don't use `foreach' here, it won't work.
+    {
+	my $this_line = shift (@lines);
+	chomp $this_line;
+
+	if ($this_line =~ /^(\s+)/) {
+	    $user_indent = $1;
+	}
+	else {
+	    $user_indent = '';
+	}
+
+	# If it matches any of the line-start regexps, print a newline now...
+	if ($suppress_line_start_match)
+	{
+	    $suppress_line_start_match = 0;
+	}
+	elsif (($this_line =~ /^(\s*)\*\s+[a-zA-Z0-9]/)
+	       || ($this_line =~ /^(\s*)\* [a-zA-Z0-9_\.\/\+-]+/)
+	       || ($this_line =~ /^(\s*)\([a-zA-Z0-9_\.\/\+-]+(\)|,\s*)/)
+	       || ($this_line =~ /^(\s+)(\S+)/)
+	       || ($this_line =~ /^(\s*)- +/)
+	       || ($this_line =~ /^()\s*$/)
+	       || ($this_line =~ /^(\s*)\*\) +/)
+	       || ($this_line =~ /^(\s*)[a-zA-Z0-9](\)|\.|\:) +/))
+	{
+	    $length_remaining = $max_line_length - (length ($user_indent));
+	}
+
+	# Now that any user_indent has been preserved, strip off leading
+	# whitespace, so up-folding has no ugly side-effects.
+	$this_line =~ s/^\s*//;
+
+	# Accumulate the line, and adjust parameters for next line.
+	my $this_len = length ($this_line);
+	if ($this_len == 0)
+	{
+	    # Blank lines should cancel any user_indent level.
+	    $user_indent = '';
+	    $length_remaining = $max_line_length;
+	}
+	elsif ($this_len >= $length_remaining) # Line too long, try breaking it.
+	{
+	    # Walk backwards from the end.  At first acceptable spot, break
+	    # a new line.
+	    my $idx = $length_remaining - 1;
+	    if ($idx < 0) { $idx = 0 };
+	    while ($idx > 0)
+	    {
+		if (substr ($this_line, $idx, 1) =~ /\s/)
+		{
+		    my $line_now = substr ($this_line, 0, $idx);
+		    my $next_line = substr ($this_line, $idx);
+		    $this_line = $line_now;
+
+		    # Clean whitespace off the end.
+		    chomp $this_line;
+
+		    # The current line is ready to be printed.
+		    $this_line .= "\n${left_pad_str}";
+
+		    # Make sure the next line is allowed full room.
+		    $length_remaining = $max_line_length - (length ($user_indent));
+
+		    # Strip next_line, but then preserve any user_indent.
+		    $next_line =~ s/^\s*//;
+
+		    # Sneak a peek at the user_indent of the upcoming line, so
+		    # $next_line (which will now precede it) can inherit that
+		    # indent level.  Otherwise, use whatever user_indent level
+		    # we currently have, which might be none.
+		    my $next_next_line = shift (@lines);
+		    if ((defined ($next_next_line)) && ($next_next_line =~ /^(\s+)/)) {
+			$next_line = $1 . $next_line if (defined ($1));
+			# $length_remaining = $max_line_length - (length ($1));
+			$next_next_line =~ s/^\s*//;
+		    }
+		    else {
+			$next_line = $user_indent . $next_line;
+		    }
+		    if (defined ($next_next_line)) {
+			unshift (@lines, $next_next_line);
+		    }
+		    unshift (@lines, $next_line);
+
+		    # Our new next line might, coincidentally, begin with one of
+		    # the line-start regexps, so we temporarily turn off
+		    # sensitivity to that until we're past the line.
+		    $suppress_line_start_match = 1;
+
+		    last;
+		}
+		else
+		{
+		    $idx--;
+		}
+	    }
+
+	    if ($idx == 0)
+	    {
+		# We bottomed out because the line is longer than the
+		# available space.  But that could be because the space is
+		# small, or because the line is longer than even the maximum
+		# possible space.  Handle both cases below.
+
+		if ($length_remaining == ($max_line_length - (length ($user_indent))))
+		{
+		    # The line is simply too long -- there is no hope of ever
+		    # breaking it nicely, so just insert it verbatim, with
+		    # appropriate padding.
+		    $this_line = "\n${left_pad_str}${this_line}";
+		}
+		else
+		{
+		    # Can't break it here, but may be able to on the next round...
+		    unshift (@lines, $this_line);
+		    $length_remaining = $max_line_length - (length ($user_indent));
+		    $this_line = "\n${left_pad_str}";
+		}
+	    }
+	}
+	else  # $this_len < $length_remaining, so tack on what we can.
+	{
+	    # Leave a note for the next iteration.
+	    $length_remaining = $length_remaining - $this_len;
+
+	    if ($this_line =~ /\.$/)
+	    {
+		$this_line .= "  ";
+		$length_remaining -= 2;
+	    }
+	    else  # not a sentence end
+	    {
+		$this_line .= " ";
+		$length_remaining -= 1;
+	    }
+	}
+
+	# Unconditionally indicate that loop has run at least once.
+	$first_time = 0;
+
+	$wrapped_text .= "${user_indent}${this_line}";
+    }
+
+    # One last bit of padding.
+    $wrapped_text .= "\n";
+
+    return $wrapped_text;
+}
+
+# main
+
+my @date;
+my $author;
+my @files;
+my $comment;
+my $merge;
+
+my $state; # 0-header 1-comment 2-files
+my $done = 0;
+
+$state = 0;
+
+while (<>) {
+    #print STDERR "debug ($state, " . (@date ? (strftime "%Y-%m-%d", @date) : "") . "): `$_'\n";
+
+    if ($state == 0) {
+	if (m,^Author: (.*),) {
+	    $author = $1;
+	}
+	if (m,^Date: (.*),) {
+	    @date = strptime($1);
+	}
+	if (m,^Merge: (.*),) {
+	    $merge = 1;
+	}
+	$state = 1 if (m,^$,);
+    } elsif ($state == 1) {
+	$state = 2 if (m,^$,);
+	s/^    //g;
+	s/\n/ /g;
+	$comment = $comment . $_;
+    } elsif ($state == 2 && $merge) {
+	$done = 1;
+    } elsif ($state == 2) {
+	if (m,^([-0-9]+)\t([-0-9]+)\t(.*)$,) {
+	    push @files, $3;
+	} elsif (m,^[^ ],) {
+	    # No file changes.
+	    $done = 1;
+	}
+	$done = 1 if (m,^$,);
+    }
+
+    if ($done && @date == ()) {
+	print STDERR "warning: could not parse entry\n";
+    } elsif ($done) {
+	print (strftime "%Y-%m-%d  $author\n\n", @date);
+
+	my $files = join (", ", @files);
+	$files = mywrap ("\t", "\t", "* $files"), ": ";
+
+	if (index($comment, EMPTY_LOG_MESSAGE) > -1 ) {
+	    $comment = "[no log message]\n";
+	}
+
+	my $files_last_line_len = 0;
+	$files_last_line_len = last_line_len($files) + 1;
+	my $msg = wrap_log_entry($comment, "\t", 69-$files_last_line_len, 69);
+
+	$msg =~ s/[ \t]+\n/\n/g;
+
+	if ($merge) {
+	    print "\t$msg\n";
+	} else {
+	    print "$files: $msg\n";
+	}
+
+	@date = ();
+	$author = "";
+	@files = ();
+	$comment = "";
+	$merge = 0;
+
+	$state = 0;
+	$done = 0;
+    }
+}
+
+if (@files) {
+    print (strftime "%Y-%m-%d  $author\n\n", @date);
+    my $msg = wrap_log_entry($comment, "\t", 69, 69);
+    $msg =~ s/[ \t]+\n/\n/g;
+    print "\t* $msg\n";
+}
diff --git a/config/p4est_include.m4 b/config/p4est_include.m4
new file mode 100644
index 0000000..6be224f
--- /dev/null
+++ b/config/p4est_include.m4
@@ -0,0 +1,76 @@
+dnl
+dnl p4est_include.m4 - custom macros
+dnl
+
+dnl Documentation for macro names: brackets indicate optional arguments
+
+dnl P4EST_ARG_ENABLE(NAME, COMMENT, TOKEN)
+dnl Check for --enable/disable-NAME using shell variable P4EST_ENABLE_TOKEN
+dnl If shell variable is set beforehand it overrides the option
+dnl If enabled, define TOKEN to 1 and set conditional P4EST_TOKEN
+dnl Default is disabled
+dnl
+AC_DEFUN([P4EST_ARG_ENABLE],
+         [SC_ARG_ENABLE_PREFIX([$1], [$2], [$3], [P4EST])])
+
+dnl P4EST_ARG_DISABLE(NAME, COMMENT, TOKEN)
+dnl Check for --enable/disable-NAME using shell variable P4EST_ENABLE_TOKEN
+dnl If shell variable is set beforehand it overrides the option
+dnl If enabled, define TOKEN to 1 and set conditional P4EST_TOKEN
+dnl Default is enabled
+dnl
+AC_DEFUN([P4EST_ARG_DISABLE],
+         [SC_ARG_DISABLE_PREFIX([$1], [$2], [$3], [P4EST])])
+
+dnl P4EST_ARG_WITH(NAME, COMMENT, TOKEN)
+dnl Check for --with/without-NAME using shell variable P4EST_WITH_TOKEN
+dnl If shell variable is set beforehand it overrides the option
+dnl If with, define TOKEN to 1 and set conditional P4EST_TOKEN
+dnl Default is without
+dnl
+AC_DEFUN([P4EST_ARG_WITH],
+         [SC_ARG_WITH_PREFIX([$1], [$2], [$3], [P4EST])])
+
+dnl P4EST_ARG_WITHOUT(NAME, COMMENT, TOKEN)
+dnl Check for --with/without-NAME using shell variable P4EST_WITH_TOKEN
+dnl If shell variable is set beforehand it overrides the option
+dnl If with, define TOKEN to 1 and set conditional P4EST_TOKEN
+dnl Default is with
+dnl
+AC_DEFUN([P4EST_ARG_WITHOUT],
+         [SC_ARG_WITHOUT_PREFIX([$1], [$2], [$3], [P4EST])])
+
+dnl P4EST_CHECK_LIBRARIES(PREFIX)
+dnl This macro bundles the checks for all libraries and link tests
+dnl that are required by p4est.  It can be used by other packages that
+dnl link to p4est to add appropriate options to LIBS.
+dnl
+AC_DEFUN([P4EST_CHECK_LIBRARIES],
+[
+P4EST_CHECK_METIS([$1])
+P4EST_CHECK_PETSC([$1])
+])
+
+dnl P4EST_AS_SUBPACKAGE(PREFIX)
+dnl Call from a package that is using p4est as a subpackage.
+dnl Sets PREFIX_DIST_DENY=yes if p4est is make install'd.
+dnl
+AC_DEFUN([P4EST_AS_SUBPACKAGE],
+         [SC_ME_AS_SUBPACKAGE([$1], [m4_tolower([$1])], [P4EST], [p4est])])
+
+dnl P4EST_FINAL_MESSAGES(PREFIX)
+dnl This macro prints messages at the end of the configure run.
+dnl
+AC_DEFUN([P4EST_FINAL_MESSAGES],
+[
+if test "x$$1_ENABLE_VTK_COMPRESSION" = xyes && \
+   test "x$$1_HAVE_ZLIB" != xyes ; then
+AC_MSG_NOTICE([- $1 -------------------------------------------------
+VTK compression is enabled, but we did not find a recent zlib.
+This is OK if the following does not matter to you:
+Calling p4est_vtk_write_file will abort your program.
+You can fix this by compiling a working zlib and pointing LIBS to it,
+or by using the p4est configure option --disable-vtk-zlib.
+])
+fi
+])
diff --git a/config/p4est_metis.m4 b/config/p4est_metis.m4
new file mode 100644
index 0000000..d1d9073
--- /dev/null
+++ b/config/p4est_metis.m4
@@ -0,0 +1,49 @@
+
+dnl P4EST_CHECK_METIS(PREFIX)
+dnl Check for the METIS library and link a test program
+dnl
+AC_DEFUN([P4EST_CHECK_METIS], [
+
+AC_MSG_CHECKING([for metis])
+
+SC_ARG_WITH_PREFIX([metis], [enable metis-dependent code], [METIS], [$1])
+if test "x$$1_WITH_METIS" != xno ; then
+  $1_METIS_INC=
+  $1_METIS_LD=
+  $1_METIS_LIB="-lmetis"
+  if test "x$$1_WITH_METIS" != xyes ; then
+    $1_METIS_INC="-I$$1_WITH_METIS/include"
+    $1_METIS_LD="-L$$1_WITH_METIS/lib"
+  fi
+  PRE_METIS_CPPFLAGS="$CPPFLAGS"
+  CPPFLAGS="$CPPFLAGS $$1_METIS_INC"
+  PRE_METIS_LDFLAGS="$LDFLAGS"
+  LDFLAGS="$LDFLAGS $$1_METIS_LD"
+  PRE_METIS_LIBS="$LIBS"
+  LIBS="$$1_METIS_LIB $LIBS"
+
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <metis.h>]],
+[[
+ int n = 0, xadj, adjncy, adjwgt, vwgt;
+ int nparts = 0, options = 0, volume, part, ncon, vsize;
+ float tpwgts, ubvec;
+
+ METIS_PartGraphRecursive (&n, &ncon, &xadj, &adjncy, &vwgt, &vsize,
+                           &adjwgt, &nparts, &tpwgts, &ubvec, &options,
+                           &volume, &part);
+]])],,
+                 [AC_MSG_ERROR([Unable to link metis])])
+dnl Keep the variables changed as done above
+dnl CPPFLAGS="$PRE_METIS_CPPFLAGS"
+dnl LDFLAGS="$PRE_METIS_LDFLAGS"
+dnl LIBS="$PRE_METIS_LIBS"
+
+  AC_MSG_RESULT([successful])
+else
+  AC_MSG_RESULT([not used])
+fi
+
+dnl No AC_SUBST since we're changing variables directly
+dnl AC_SUBST([$1_METIS_LIBS])
+dnl AC_SUBST([$1_METIS_INCLUDES])
+])
diff --git a/config/p4est_petsc.m4 b/config/p4est_petsc.m4
new file mode 100644
index 0000000..6925889
--- /dev/null
+++ b/config/p4est_petsc.m4
@@ -0,0 +1,75 @@
+
+dnl P4EST_CHECK_PETSC(PREFIX)
+dnl Check for the PETSc library and link a test program
+dnl portions adapted from libmesh petsc.m4
+dnl
+AC_DEFUN([P4EST_CHECK_PETSC], [
+
+AC_MSG_CHECKING([for PETSc])
+
+SC_ARG_WITH_PREFIX([petsc], [enable PETSc-dependent code], [PETSC], [$1])
+$1_PETSC_INCLUDE_DIRS=
+$1_PETSC_LINK_LIBS=
+if test "x$$1_WITH_PETSC" != xno ; then
+  # use the PETSC_DIR environment variable by default
+  $1_PETSC_DIR="$PETSC_DIR"
+  $1_PETSC_ARCH="$PETSC_ARCH"
+  if test "x$$1_WITH_PETSC" != xyes ; then
+    $1_PETSC_DIR="$$1_WITH_PETSC"
+  fi
+  if (test "x$$1_PETSC_DIR" = x); then
+    AC_PATH_PROG(PETSCARCH, petscarch)
+    if (test "x$PETSCARCH" != x); then
+      $1_PETSC_DIR=/usr/lib/petsc
+    fi
+  fi
+  if test ! -r $$1_PETSC_DIR/include/petscversion.h ; then
+    AC_MSG_ERROR([Unable to find readable petscversion.h])
+  fi
+  $1_PETSC_MAJOR=`grep "define PETSC_VERSION_MAJOR" $$1_PETSC_DIR/include/petscversion.h | sed -e "s/#define PETSC_VERSION_MAJOR[ ]*//g"`
+  if test "$$1_PETSC_MAJOR" -lt 3 ; then
+    AC_MSG_ERROR([PETSc version >= 3.0 required])
+  fi
+  if test -r $$1_PETSC_DIR/makefile ; then
+    $1_PETSC_LINK_LIBS=`make -s -C $$1_PETSC_DIR getlinklibs`
+    $1_PETSC_INCLUDE_DIRS=`make -s -C $$1_PETSC_DIR getincludedirs`
+  elif test -r $$1_PETSC_DIR/conf/variables ; then
+    if ! test -r $$1_PETSC_DIR/conf/rules ; then
+      AC_MSG_ERROR([Unable to find $$1_PETSC_DIR/makefile or $$1_PETSC_DIR/conf/rules])
+    fi
+    cat <<EOF >Makefile_config_petsc
+include $$1_PETSC_DIR/conf/variables
+include $$1_PETSC_DIR/conf/rules
+EOF
+    $1_PETSC_LINK_LIBS=`make -s -f Makefile_config_petsc getlinklibs`
+    $1_PETSC_INCLUDE_DIRS=`make -s -f Makefile_config_petsc getincludedirs`
+    rm -f Makefile_config_petsc
+  else 
+    AC_MSG_ERROR([Unable to find $$1_PETSC_DIR/makefile or $$1_PETSC_DIR/conf/variables])
+  fi
+  PRE_PETSC_CPPFLAGS="$CPPFLAGS"
+  CPPFLAGS="$CPPFLAGS $$1_PETSC_INCLUDE_DIRS"
+  PRE_PETSC_LIBS="$LIBS"
+  LIBS="$LIBS $$1_PETSC_LINK_LIBS"
+
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <petsc.h>]],
+[[
+  PetscErrorCode ierr;
+
+  ierr = PetscInitialize (NULL, NULL, NULL, NULL);CHKERRQ(ierr);
+  ierr = PetscFinalize();
+  return 0;
+]])],,
+                 [AC_MSG_ERROR([Unable to link petsc])])
+  CPPFLAGS="$PRE_PETSC_CPPFLAGS"
+  LIBS="$PRE_PETSC_LIBS"
+
+  AC_MSG_RESULT([successful])
+else
+  AC_MSG_RESULT([not used])
+fi
+
+AC_SUBST([$1_PETSC_INCLUDE_DIRS])
+AC_SUBST([$1_PETSC_LINK_LIBS])
+])
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..485edbd
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,105 @@
+dnl
+dnl This file is part of p4est.
+dnl
+
+AC_INIT([p4est],
+        [m4_esyscmd([build-aux/git-version-gen .tarball-version])],
+        [p4est at librelist.com])
+AC_PREREQ(2.61)
+AC_CONFIG_HEADERS([src/pre_config.h])
+AC_CONFIG_SRCDIR([src/p4est.h])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CONFIG_MACRO_DIR([config])
+AC_PREFIX_DEFAULT([$PWD/local])
+AX_PREFIX_CONFIG_H([src/p4est_config.h])
+AM_INIT_AUTOMAKE([parallel-tests subdir-objects])
+AM_SILENT_RULES
+SC_VERSION([P4EST])
+
+echo "o---------------------------------------"
+echo "| Checking options"
+echo "o---------------------------------------"
+
+P4EST_ARG_ENABLE([debug], [enable debug mode (assertions and extra checks)],
+                 [DEBUG])
+P4EST_ARG_ENABLE([vtk-doubles], [use doubles for vtk file data],
+                 [VTK_DOUBLES])
+P4EST_ARG_DISABLE([vtk-binary], [write vtk ascii file data],
+                  [VTK_BINARY])
+P4EST_ARG_DISABLE([vtk-zlib], [disable zlib compression for vtk binary data],
+                  [VTK_COMPRESSION])
+P4EST_ARG_DISABLE([2d], [disable the 2D library], [BUILD_2D])
+P4EST_ARG_DISABLE([3d], [disable the 3D library], [BUILD_3D])
+P4EST_ARG_ENABLE([p6est], [enable hybrid 2D+1D p6est library], [BUILD_P6EST])
+
+echo "o---------------------------------------"
+echo "| Checking MPI and related programs"
+echo "o---------------------------------------"
+
+dnl A nonempty second/third argument causes to enable F77+F90/CXX, respectively.
+SC_MPI_CONFIG([P4EST], [yes], [yes])
+SC_MPI_ENGAGE([P4EST])
+# This is needed for compatibility with automake >= 1.12
+m4_ifdef([AM_PROG_AR],[AM_PROG_AR])
+SC_PROG_LINT
+SC_C_VERSION
+LT_INIT
+
+echo "o---------------------------------------"
+echo "| Checking libraries"
+echo "o---------------------------------------"
+
+SC_CHECK_LIBRARIES([P4EST])
+P4EST_CHECK_LIBRARIES([P4EST])
+
+echo "o---------------------------------------"
+echo "| Checking headers"
+echo "o---------------------------------------"
+
+AC_CHECK_HEADERS([arpa/inet.h netinet/in.h unistd.h])
+
+echo "o---------------------------------------"
+echo "| Checking functions"
+echo "o---------------------------------------"
+
+AC_CHECK_FUNCS([fsync])
+
+echo "o---------------------------------------"
+echo "| Checking subpackages"
+echo "o---------------------------------------"
+
+P4EST_DIST_DENY=
+P4EST_DISTCLEAN=
+SC_AS_SUBPACKAGE([P4EST])
+AM_CONDITIONAL([P4EST_DIST_DENY], [test "x$P4EST_DIST_DENY" != x])
+AC_SUBST([P4EST_DISTCLEAN])
+
+# Print summary.
+
+AC_DEFINE_UNQUOTED(CPP,         ["${CPP}"],         [C preprocessor])
+AC_DEFINE_UNQUOTED(CPPFLAGS,    ["${CPPFLAGS}"],    [C preprocessor flags])
+AC_DEFINE_UNQUOTED(CC,          ["${CC}"],          [C compiler])
+dnl AC_DEFINE_UNQUOTED(C_VERSION,   ["${C_VERSION}"],   [C compiler version])
+AC_DEFINE_UNQUOTED(CFLAGS,      ["${CFLAGS}"],      [C compiler flags])
+AC_DEFINE_UNQUOTED(LDFLAGS,     ["${LDFLAGS}"],     [Linker flags])
+AC_DEFINE_UNQUOTED(LIBS,        ["${LIBS}"],        [Libraries])
+
+echo "o----------------------------------"
+echo "| Results for p4est are"
+echo "o----------------------------------"
+echo "| CPP:         $CPP"
+echo "| CPPFLAGS:    $CPPFLAGS"
+echo "| CC:          $CC"
+dnl echo "| C_VERSION:   $C_VERSION"
+echo "| CFLAGS:      $CFLAGS"
+echo "| LDFLAGS:     $LDFLAGS"
+echo "| LIBS:        $LIBS"
+echo "o----------------------------------"
+
+# Create output files.
+AC_CONFIG_FILES([Makefile Makefile.p4est.pre Doxyfile])
+AC_OUTPUT
+
+# Final messages.
+SC_FINAL_MESSAGES([P4EST])
+P4EST_FINAL_MESSAGES([P4EST])
diff --git a/doc/attic/balance.txt b/doc/attic/balance.txt
new file mode 100644
index 0000000..f67da61
--- /dev/null
+++ b/doc/attic/balance.txt
@@ -0,0 +1,68 @@
+1. Assemble list of quadrants to send.
+Loop over trees: t
+	Locally balance tree
+	If tree completely on this processor and no tree contact
+		skip t
+	Loop over quadrants: q
+		If 3x3 nbrhood of q is inside unit cube and on this processor
+			skip q
+		Loop over nbrhood quadrants of q: s
+			skip s if s == q
+			If s is outside unit cube
+				If s is across face
+					Transf/sched q/s across face
+				Else if s is across edge
+					Transf/sched q/s for all edges
+				Else (s is across corner)
+					Transf/sched q/s for all corners
+			Else
+				Schedule q/s for same tree
+
+2. Find communication pattern via sc_ranges_* functions (allgather)
+
+3. Process first round of quadrants
+Loop over all peers p excluding myself
+	Isend first count
+	If count > 0
+		Sort send array
+		Isend first load
+		Irecv second count
+Loop over all peers from which to possibly receive
+	Irecv first count
+While pending first receives
+	For all peers in Waitsome p
+		If receiving first count
+			If first count > 0
+				Irecv first load
+		Else (receiving first load)
+			Compute response quadrants for p
+			Isend second count
+			If second count > 0
+				Isend second load
+Emulate send and receive operations for myself (across trees)
+
+4. Process second round of quadrants
+While pending second receives
+	For all peers in Waitsome
+		If receiving second count
+			If count > 0
+				Irecv second load
+		Else (receiving second load)
+
+5. Merge received quadrants from both rounds
+Loop over peers p (including myself)
+	If count == 0
+		skip p
+	Loop over received quadrants q
+		If q not in my tree range
+			Skip q (came from corner/edge cross bounce)
+		Append q to the quadrants of q->piggy_tree
+Loop over local trees t
+	If tree not fully owned or any cross-tree contact
+		Sort quadrants
+		Locally balance tree
+
+6. Cleanup
+Waitall
+Update quadrant statistics
+Free memory
diff --git a/doc/attic/balance_helpers.txt b/doc/attic/balance_helpers.txt
new file mode 100644
index 0000000..974b5ab
--- /dev/null
+++ b/doc/attic/balance_helpers.txt
@@ -0,0 +1,37 @@
+1. Balance schedule (q my quadrant, s insulation quadrant != q)
+Find owners of first and last descendents of s
+	(i.e. what peers intersect my insulation zone?)
+For all owners between and including these two processors p
+	Schedule q to be sent to p (don't include duplicates)
+
+2. Balance response (after receiving first load)
+Loop over all received quadrants
+	Append each tree id uniquely to an array (will be in tree order)
+Loop over all received tree ids
+	Compute tree overlap (this is slow when called once per tree!)
+Uniqify tree overlap
+
+3. Compute tree overlap (currently called once per tree t)
+Loop over all received quadrants q
+	If q not in t, skip q (this consumes the majority of cycles)
+	If q is not in unit cube (comes from a different tree)
+	Loop over 3x3 neighborhood of q: s
+		If s is outside the unit cube, skip s
+		Compute first and last descendents of s: fd, ld
+		If ld < t->fd or t->ld < fd (no intersection), skip s
+		If fd <= t->fd (s overlaps beginning of t)
+			first_index = 0
+		Else
+			Find lowest quadrant >= s: first_index
+			If not found, skip s
+		If t->ld <= ld (s overlaps end of t)
+			last_index = last quadrant of t
+		Else
+			Find highest quadrant <= ld: last_index (must exist)
+		If first_index > last_index, skip s
+		Copy everything between first_index, last_index into output
+			(This is done multiply for edges and corners)
+
+4. Uniqify tree overlap
+	Sort output list
+	Remove duplicates
diff --git a/doc/attic/checksums.txt b/doc/attic/checksums.txt
new file mode 100644
index 0000000..d12f9b0
--- /dev/null
+++ b/doc/attic/checksums.txt
@@ -0,0 +1,20 @@
+
+This files holds various checksums of example runs for reference.
+If they ever change, something is badly wrong!
+
+    np    |   example   |   parameters   |   crc
+----------o-------------o----------------o------------
+        1 | timings     | unit 10        | 0x6e3e83c4
+        1 | timings     | unit 11        | 0x334bc3de
+       64 | timings     | unit 14        | 0xad908ce4
+      256 | timings     | unit 15        | 0x9e7da646
+     1, 4 | timings     | star 6         | 0x14107b57
+       52 | timings     | star 13        | 0xc86c74d9
+       64 | timings     | star 13        | 0xc86c74d9
+  1, 2, 3 | simple      | three 7        | 0xa8d85863
+        4 | simple      | three 7        | 0x20fb58ed
+  1, 3, 5 | simple      | moebius 6      | 0x98ab6cb2
+        6 | simple      | moebius 6      | 0x6d2d6d6c
+        5 | simple      | star 6         | 0x38d3736f
+     1, 3 | simple      | periodic 6     | 0x9dd600c5
+        5 | second      |                | 0x324eb631
diff --git a/doc/attic/connectivity.txt b/doc/attic/connectivity.txt
new file mode 100644
index 0000000..eb0d344
--- /dev/null
+++ b/doc/attic/connectivity.txt
@@ -0,0 +1,24 @@
+This file covers the 3D connectivity logic and the transforms from _bits.
+
+1. p8est_find_face_transform (given my tree and my face)
+Compute target tree, face and orientation from connectivity.
+If ttree == tree and tface == face (domain boundary), return -1
+Compute my_axis[0] and [1] in 0..2 parallel to the face edges
+Compute permuted corners for face corners 0, 1 and 2
+Determine if either or both axis connections are flipped
+Loop over all 12 edges e
+	If corners of e match corners of axis 0, save e as target axis 0
+	If corners of e match corners of axis 1, save e as target axis 1
+Store face combination in transform[8]
+
+2. p8est_quadrant_transform_face q into r
+If q is an unclamped node, set mh to 0
+Else set mh to negative length of q
+Assign root_length + mh and 2 * root_length + mh (which can overflow ok)
+Compute axis 0 and axis 1 coordinates of r (flipping if applicable)
+Based on the axis 2 combination, transform axis 2 (4 cases possible)
+Copy level from q to r
+
+3. p8est_find_edge_transform
+
+5. p8est_find_corner_transform
diff --git a/doc/attic/p8est_trilinear.c b/doc/attic/p8est_trilinear.c
new file mode 100644
index 0000000..119a25c
--- /dev/null
+++ b/doc/attic/p8est_trilinear.c
@@ -0,0 +1,42 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p8est_trilinear.h>
+
+void
+p8est_trilinear_mesh_destroy (trilinear_mesh_t * mesh)
+{
+  P4EST_ASSERT (mesh->destructor == p8est_trilinear_mesh_destroy);
+  P4EST_ASSERT (mesh->extra_info == NULL);
+
+  P4EST_FREE (mesh->elem_table);
+  P4EST_FREE (mesh->node_table);
+  P4EST_FREE (mesh->fvnid_count_table);
+  P4EST_FREE (mesh->fvnid_interval_table);
+  sc_mempool_destroy (mesh->sharer_pool);
+
+  P4EST_FREE (mesh->elem_pids);
+  P4EST_FREE (mesh->node_pids);
+
+  P4EST_FREE (mesh);
+}
diff --git a/doc/attic/p8est_trilinear.h b/doc/attic/p8est_trilinear.h
new file mode 100644
index 0000000..d35af77
--- /dev/null
+++ b/doc/attic/p8est_trilinear.h
@@ -0,0 +1,344 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * This file provides an interface that is compatible
+ * with the trilinear finite elements used in the Rhea code.
+ */
+
+#ifndef P8EST_TRILINEAR_H
+#define P8EST_TRILINEAR_H
+
+#include <p8est_nodes.h>
+#include <p8est_lnodes.h>
+
+/*
+ * BEGIN verbatim copy of trilinear_mesh_types.h from the Rhea code
+ */
+
+#ifndef TRILINEAR_MESH_TYPES_H
+#define TRILINEAR_MESH_TYPES_H
+
+#include <sc_containers.h>
+
+/*
+ * This file contains typedefs and macros related to the trilinear mesh.
+ * It should not contain specific dependencies to either octor of p4est.
+ */
+
+#ifndef OCTOR_TICKT_TYPES
+#define OCTOR_TICKT_TYPES
+
+typedef int32_t     tick_t;
+
+typedef struct point_t
+{
+  tick_t              x, y, z;
+}
+point_t;
+
+#endif /* !OCTOR_TICKT_TYPES */
+
+typedef int16_t     trilinear_mesh_pid_t;
+
+typedef enum
+{
+  ANCHORED = 0,
+  DANGLING_ON_XEDGE = -1,
+  DANGLING_ON_YEDGE = -2,
+  DANGLING_ON_ZEDGE = -3,
+  DANGLING_ON_XFACE = -4,
+  DANGLING_ON_YFACE = -5,
+  DANGLING_ON_ZFACE = -6
+}
+trilinear_node_type_t;
+
+typedef struct trilinear_elem_t
+{
+  int32_t             local_node_id[8]; /* indices into local node table */
+  tick_t              lx, ly, lz;       /* lower-left element coordinates */
+  tick_t              size;     /* size of the element in ticks */
+  void               *data;     /* pointer to its record, managed by octor */
+}
+trilinear_elem_t;
+
+/**
+ * int32link_t: linked list entry recording processor ids
+ */
+typedef struct int32link_t
+{
+  int32_t             id;
+  struct int32link_t *next;
+}
+int32link_t;
+
+/**
+ * trilinear_anode_t: anchored node (associated with free variables)
+ */
+typedef struct trilinear_anode_t
+{
+  point_t             point;
+  int64_t             fvnid;
+  int32link_t        *share;    /* processors that share this anchored node */
+}
+trilinear_anode_t;
+
+typedef struct trilinear_dnode_t
+{
+  point_t             point;
+  int32_t             type;
+  int32_t             local_anode_id[4];        /* _id[2] is -1 for an edge node */
+}
+trilinear_dnode_t;
+
+typedef union trilinear_node_t
+{
+  point_t             point;
+  trilinear_anode_t   anchored;
+  trilinear_dnode_t   dangling;
+}
+trilinear_node_t;
+
+/* *INDENT-OFF* */
+/* define below the smallest integer type that holds these flags */
+typedef enum trilinear_boundary_enum
+{
+  TRILINEAR_BOUNDARY_NONE       =      0,
+  TRILINEAR_BOUNDARY_IS_LEFT    = 0x0001,
+  TRILINEAR_BOUNDARY_IS_RIGHT   = 0x0002,
+  TRILINEAR_BOUNDARY_IS_FRONT   = 0x0004,
+  TRILINEAR_BOUNDARY_IS_BACK    = 0x0008,
+  TRILINEAR_BOUNDARY_IS_BOTTOM  = 0x0010,
+  TRILINEAR_BOUNDARY_IS_TOP     = 0x0020,
+  TRILINEAR_BOUNDARY_IS_EDGE    = 0x0040,
+  TRILINEAR_BOUNDARY_IS_CORNER  = 0x0080,
+  TRILINEAR_BOUNDARY_IS_ORIGIN  = 0x0100,
+  TRILINEAR_BOUNDARY_IS_3EDGE   = 0x0200,
+  TRILINEAR_BOUNDARY_IS_3CORNER = 0x0400,
+  TRILINEAR_BOUNDARY_IS_PRDCX   = 0x0800,
+  TRILINEAR_BOUNDARY_IS_PRDCY   = 0x1000,
+  TRILINEAR_BOUNDARY_IS_PRDCZ   = 0x2000,
+
+  TRILINEAR_BOUNDARY_IS_XBC    = (TRILINEAR_BOUNDARY_IS_LEFT |
+                                  TRILINEAR_BOUNDARY_IS_RIGHT),
+  TRILINEAR_BOUNDARY_IS_YBC    = (TRILINEAR_BOUNDARY_IS_FRONT |
+                                  TRILINEAR_BOUNDARY_IS_BACK),
+  TRILINEAR_BOUNDARY_IS_ZBC    = (TRILINEAR_BOUNDARY_IS_BOTTOM |
+                                  TRILINEAR_BOUNDARY_IS_TOP),
+  TRILINEAR_BOUNDARY_IS_FACE   = (TRILINEAR_BOUNDARY_IS_XBC |
+                                  TRILINEAR_BOUNDARY_IS_YBC |
+                                  TRILINEAR_BOUNDARY_IS_ZBC)
+}
+trilinear_boundary_enum_t;
+/* *INDENT-ON* */
+
+/* this integer is big enough to hold a trilinear_boundary_enum_t */
+typedef uint16_t    trilinear_boundary_flag_t;
+
+/**
+ * trilinear_element_info2: a structure precomputed after mesh extraction.
+ * First come the anchored nodes, then the dangling nodes.
+ * Variables beginning with Q are over direct and indirect anchored nodes (iqc).
+ * Variable dQcolumn is indexed by dangling nodes only as
+ * dQcolumn[nd][l] = iqc of depended anchored node l.
+ * If interior_anchors_only is nonzero, then the element has only
+ * anchored nodes as corners that are not on the domain boundary.
+ */
+typedef struct trilinear_element_info2
+{
+  int8_t              nanchored, ndangling;
+  int8_t              interior_only, interior_anchors_only;
+  int8_t              corner[8];
+  int8_t              Qisdirect[8];
+  int32_t             Qindices[8];
+  int64_t             Qfvnids[8];
+  trilinear_boundary_flag_t Qboundary[8];
+  int8_t              dQcolumn[8][4];
+}
+trilinear_element_info2_t;
+
+typedef struct trilinear_mesh_extra
+{
+  int32_t             shared_elem_num;
+  int32_t            *shared_elem_ids;
+  trilinear_element_info2_t *info2;
+}
+trilinear_mesh_extra_t;
+
+/**
+ * trilinear_mesh_t: main mesh structure used in Rhea
+ */
+typedef struct trilinear_mesh_t
+{
+  /* Global mesh statistics */
+  int64_t             total_elem_num;
+  int64_t             total_node_num;
+  int64_t             total_anode_num;
+  int64_t             total_dnode_num;
+
+  /* Local mesh parameters */
+  int32_t             local_elem_num;   /* number of element on this processor */
+  int32_t             local_node_num;   /* number of anchored and dangling nodes */
+
+  /* The first part of the node table contains anchored nodes. The free
+     variables (anchored nodes) owned by a processor is clustered together
+     with no holes within the first part of the table.
+
+     The second part of the node table contains dangling nodes. */
+
+  int32_t             local_anode_num;  /* number of anchored nodes */
+  int32_t             local_onode_num;  /* number of owned anchored nodes */
+  int32_t             local_dnode_num;  /* number of dangling nodes */
+
+  int32_t             local_owned_offset;       /* offset to the first
+                                                   owned anchored node */
+
+  /* Memory allocated by Octor to hold the trilinear elements and nodes */
+  trilinear_elem_t   *elem_table;
+  trilinear_node_t   *node_table;
+
+  /* Memory allocated for free variable interval table. Has
+     (groupsize + 1) entries. The last entry records the total
+     number of free variables plus 1 */
+  int64_t            *fvnid_count_table;
+  int64_t            *fvnid_interval_table;
+  int64_t            *all_fvnid_start;
+
+  /* convenience variables pointing to the node table.
+     Don't try to free the memory pointed to */
+  trilinear_node_t   *anode_table;
+  trilinear_node_t   *onode_table;
+  trilinear_node_t   *dnode_table;
+
+  /* convenience variables recording the total number of free variables,
+     the starting id and the ending id. identical on all processors */
+  int64_t             global_fvnid_num; /* total number of fvnids */
+  int64_t             global_fvnid_start;       /* first global fvnid */
+  int64_t             global_fvnid_end; /* last global fvnid */
+
+  tick_t              bounds[3][2];
+  tick_t              sizes[3], minsize, maxsize;
+  double              ticksize;
+
+  sc_MPI_Comm         mpicomm;
+  int32_t             mpisize, mpirank;
+  int32_t             recsize;
+
+  /* geometry type and element and node patch ids */
+  int8_t              gid;
+  trilinear_mesh_pid_t *elem_pids;
+  trilinear_mesh_pid_t *node_pids;
+
+  /* placeholder pointers */
+  void                (*destructor) (struct trilinear_mesh_t *);
+  trilinear_mesh_extra_t *extra_info;
+
+  /* this is used in p4est only and must not be touched */
+  sc_mempool_t       *sharer_pool;      /* allocator for node sharers */
+}
+trilinear_mesh_t;
+
+/**
+ * octor_neighbor_t:
+ *
+ * The face neighbors are ordered in -x, +x, -y, +y, -z, and +z directions.
+ *
+ * For each direction, the neighbor(s) may be:
+ *
+ * 1. Out of the domain.
+ *
+ *      face_neighbor_eid[direction][0] = -1.
+ *      face_neighbor_eid[direction][1--3] = -1.
+ *
+ * 2. As large or twice as large as the current element:
+ *
+ *      face_neighbor_eid[direction][0] = index
+ *
+ *    where ((index >= 0) && (index < local_elem_num)) if the neighbor is
+ *    LOCAL, or
+ *    ((index >= local_elem_num) &&
+ *    (index < (local_elem_num + phantom_elem_num))) if the neighor is
+ *    REMOTE.
+ *
+ *
+ *      face_neighbor_eid[direction][1--3] = -1.
+ *
+ * 3. Half as large as the current element:
+ *
+ *      face_neighbor_eid[direction][i] = index
+ *
+ *    where index is defined as above. Note that in this case all four
+ *    neighbors (half as large) must exist.
+ *
+ */
+
+typedef struct octor_neighor_t
+{
+  int32_t             face_neighbor_eid[6][4];
+}
+octor_neighbor_t;
+
+typedef struct phantom_elem_t
+{
+  tick_t              lx, ly, lz;
+  tick_t              size;
+  int32_t             owner_procid;     /* remote processor id */
+  int32_t             reid;     /* element index on the remote processor  */
+}
+phantom_elem_t;
+
+typedef struct octor_neighborhood_t
+{
+  int32_t             phantom_elem_num;
+  phantom_elem_t     *phantom_elem_table;
+
+  octor_neighbor_t   *local_elem_neighbor_table;
+}
+octor_neighborhood_t;
+
+#endif /* !TRILINEAR_MESH_TYPES_H */
+
+/*
+ * END verbatim copy of trilinear_mesh_types.h from the Rhea code
+ */
+
+SC_EXTERN_C_BEGIN;
+
+/** Creates a trilinear mesh structure from a p8est and its node data.
+ */
+trilinear_mesh_t   *p8est_trilinear_mesh_new_from_nodes (p8est_t * p8est,
+                                                         p8est_nodes_t *
+                                                         nodes);
+
+/** Creates a trilinear mesh structure from a p8est and its lnode data.
+ */
+trilinear_mesh_t   *p8est_trilinear_mesh_new_from_lnodes (p8est_t * p8est,
+                                                          p8est_lnodes_t *
+                                                          lnodes);
+/** Frees a trilinear mesh structure.
+ */
+void                p8est_trilinear_mesh_destroy (trilinear_mesh_t * mesh);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_TRILINEAR_H */
diff --git a/doc/attic/p8est_trilinear_lnodes.c b/doc/attic/p8est_trilinear_lnodes.c
new file mode 100644
index 0000000..76355ce
--- /dev/null
+++ b/doc/attic/p8est_trilinear_lnodes.c
@@ -0,0 +1,619 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include <p8est_bits.h>
+#include <p8est_trilinear.h>
+
+static void
+p8est_mesh_fcode_to_ntype (p8est_lnodes_code_t face_code, int8_t node_type[])
+{
+  int                 i, c;
+  int                 edge[12];
+  int                 face[6];
+
+  memset (node_type, 0, 8);
+
+  if (p8est_lnodes_decode (face_code, face, edge)) {
+    for (i = 0; i < 6; i++) {
+      if (face[i] >= 0) {
+        c = p8est_face_corners[i][3 - face[i]];
+        node_type[c] = 1;
+      }
+    }
+    for (i = 0; i < 12; i++) {
+      if (edge[i] >= 0 && edge[i] < 4) {
+        c = p8est_edge_corners[i][1 - (edge[i] % 2)];
+        node_type[c] = 2;
+      }
+    }
+  }
+}
+
+static void
+p8est_mesh_points (point_t * points, trilinear_mesh_pid_t * pids,
+                   p4est_locidx_t * local_nodes,
+                   p4est_locidx_t num_local_nodes, p4est_lnodes_t * nodes,
+                   p4est_t * p4est)
+{
+  p4est_locidx_t      nin = nodes->num_local_nodes;
+  p4est_locidx_t      owned_offset = 0;
+  p4est_locidx_t      owned_count = nodes->owned_count;
+  p4est_locidx_t      elid, nid;
+  p4est_locidx_t      lz;
+  int                 qid;
+  int                 i, j, k, f, e;
+  int                 nf, nf2, ne, nc, cid;
+  int                 o, ref, set;
+  int8_t              node_type[8];
+  p8est_lnodes_code_t *face_code = nodes->face_code;
+  int                 ownerc;
+  p4est_topidx_t      t, nt, ownert;
+  p4est_topidx_t      ft = p4est->first_local_tree;
+  p4est_topidx_t      lt = p4est->last_local_tree;
+  sc_array_t         *trees = p4est->trees;
+  p4est_tree_t       *tree;
+  size_t              count, zz;
+  p4est_quadrant_t   *q, p, r, s, ownerq;
+  p4est_qcoord_t      shift;
+  p8est_connectivity_t *conn = p4est->connectivity;
+  p4est_topidx_t     *ttt = conn->tree_to_tree;
+  int8_t             *ttf = conn->tree_to_face;
+  int                 ftr[P8EST_FTRANSFORM];
+  p8est_edge_info_t   ei;
+  sc_array_t         *eta = &ei.edge_transforms;
+  p8est_edge_transform_t *et;
+  size_t              etree;
+  p8est_corner_info_t ci;
+  sc_array_t         *cta = &ci.corner_transforms;
+  p8est_corner_transform_t *ct;
+  size_t              ctree;
+
+  P4EST_ASSERT (nodes->degree == 1 && nodes->vnodes == 8);
+
+  memset (pids, -1, sizeof (trilinear_mesh_pid_t) * num_local_nodes);
+  for (nid = 0, elid = 0, t = ft; t <= lt; t++) {
+    tree = p4est_tree_array_index (trees, t);
+    count = tree->quadrants.elem_count;
+    q = p4est_quadrant_array_index (&tree->quadrants, 0);
+    for (zz = 0; zz < count; zz++, q++, elid++) {
+      p8est_mesh_fcode_to_ntype (face_code[elid], node_type);
+      /* for every corner of every quadrant */
+      for (i = 0; i < 8; i++, nid++) {
+        for (k = 0; k < 2; k++) {
+          if (k == 0) {
+            lz = local_nodes[nid];
+            /* either a node is independent or hanging */
+            P4EST_ASSERT ((node_type[i] == 0) ^ (lz >= nin));
+          }
+          /* if a corner is hanging, the point values of both the hanging node
+           * and the independent node must be computed */
+          else if (local_nodes[nid] >= nin) {
+            lz = nodes->element_nodes[nid];
+            P4EST_ASSERT (lz < nin);
+            /* if the independent node is local, it must (by the ownership
+             * rules) be touches by a local quadrant, so its point values will
+             * be set by that quadrant: the quadrant that owns that hanging node
+             * needn't bother */
+            if (lz >= owned_offset && lz - owned_offset < owned_count) {
+              continue;
+            }
+          }
+          else {
+            continue;
+          }
+          /* pids are set whenever points are: if pid is set, nothing to do */
+          if (pids[lz] >= 0) {
+            continue;
+          }
+          /* if the node is local or hanging, its location is the current
+           * corner of the current quadrant */
+          if ((lz >= owned_offset && lz - owned_offset < owned_count) ||
+              lz >= nin) {
+            shift = P8EST_QUADRANT_LEN (q->level);
+            points[lz].x = (tick_t) (q->x + ((i & 1) ? shift : 0));
+            points[lz].y = (tick_t) (q->y + (((i >> 1) & 1) ? shift : 0));
+            points[lz].z = (tick_t) (q->z + ((i >> 2) ? shift : 0));
+            pids[lz] = (trilinear_mesh_pid_t) t;
+          }
+          else {
+            if (k == 0) {
+              p8est_quadrant_corner_descendant (q, &p, i, P8EST_QMAXLEVEL);
+            }
+            /* if k == 1, then that means that the current node is indpendent,
+             * but is not touched by the current quadrant: rather, it is
+             * touched by the parent of the current quadrant */
+            else {
+              p8est_quadrant_parent (q, &r);
+              p8est_quadrant_corner_descendant (&r, &p, i, P8EST_QMAXLEVEL);
+            }
+            p4est_quadrant_corner_neighbor (&p, i, &r);
+            /* if the corner in question is in the interior of the tree, its
+             * point values are unambiguous */
+            if (p8est_quadrant_is_inside_root (&r)) {
+              shift = P8EST_QUADRANT_LEN (P8EST_QMAXLEVEL);
+              points[lz].x = (tick_t) (p.x + ((i & 1) ? shift : 0));
+              points[lz].y = (tick_t) (p.y + (((i >> 1) & 1) ? shift : 0));
+              points[lz].z = (tick_t) (p.z + ((i >> 2) ? shift : 0));
+              pids[lz] = (trilinear_mesh_pid_t) t;
+            }
+            else {
+              ownert = t;
+              ownerq = p;
+              ownerc = i;
+              /* if q->level == P8EST_QMAXLEVEL,
+               * qid and i may not be the same */
+              qid = p8est_quadrant_child_id (&p);
+              for (j = 0; j < 3; j++) {
+                f = p8est_corner_faces[i][j];
+                p8est_quadrant_face_neighbor (&p, f, &r);
+                if (p8est_quadrant_is_outside_face (&r)) {
+                  nt = ttt[t * P4EST_FACES + f];
+                  if (ownert < nt) {
+                    continue;
+                  }
+                  nf = (int) ttf[t * P4EST_FACES + f];
+                  o = nf / P8EST_FACES;
+                  nf %= P8EST_FACES;
+                  if (nt == t && nf == f) {
+                    continue;
+                  }
+                  cid = p8est_corner_face_corners[i][f];
+                  ref = p8est_face_permutation_refs[f][nf];
+                  set = p8est_face_permutation_sets[ref][o];
+                  cid = p8est_face_permutations[set][cid];
+                  nc = p8est_face_corners[nf][cid];
+                  nf2 = (int) p8est_find_face_transform (conn, t, f, ftr);
+                  P4EST_ASSERT (nf == nf2);
+                  p8est_quadrant_transform_face (&r, &s, ftr);
+                  if (nt < ownert || ((nt == ownert) &&
+                                      p8est_quadrant_compare (&s,
+                                                              &ownerq) < 0)) {
+                    ownerq = s;
+                    ownert = nt;
+                    ownerc = nc;
+                  }
+                }
+              }
+              for (j = 0; j < 3; j++) {
+                e = p8est_corner_edges[i][j];
+                p8est_quadrant_edge_neighbor (&p, e, &r);
+                if (p8est_quadrant_is_outside_edge (&r)) {
+                  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+                  p8est_find_edge_transform (conn, t, e, &ei);
+                  for (etree = 0; etree < eta->elem_count; etree++) {
+                    et = (p8est_edge_transform_t *) sc_array_index (eta,
+                                                                    etree);
+                    nt = et->ntree;
+                    if (ownert < nt) {
+                      continue;
+                    }
+                    p8est_quadrant_transform_edge (&r, &s, &ei, et, 1);
+                    ne = (int) et->nedge;
+                    if (nt < ownert || ((nt == ownert) &&
+                                        p8est_quadrant_compare (&s,
+                                                                &ownerq) <
+                                        0)) {
+                      ownerq = s;
+                      ownert = nt;
+                      ownerc = p8est_quadrant_child_id (&s);
+                      if (qid != i) {
+                        P4EST_ASSERT ((p8est_edge_corners[e][0] == i
+                                       && p8est_edge_corners[e][1] == qid) ||
+                                      (p8est_edge_corners[e][0] == qid
+                                       && p8est_edge_corners[e][1] == i));
+                        P4EST_ASSERT (p8est_edge_corners[ne][0] == ownerc ||
+                                      p8est_edge_corners[ne][1] == ownerc);
+                        ownerc = (p8est_edge_corners[ne][0] == ownerc) ?
+                          p8est_edge_corners[ne][1] :
+                          p8est_edge_corners[ne][0];
+                      }
+                    }
+                  }
+                  sc_array_reset (eta);
+                }
+              }
+              p8est_quadrant_corner_neighbor (&p, i, &r);
+              if (p8est_quadrant_is_outside_corner (&r)) {
+                sc_array_init (cta, sizeof (p8est_corner_transform_t));
+                p8est_find_corner_transform (conn, t, i, &ci);
+                for (ctree = 0; ctree < cta->elem_count; ctree++) {
+                  ct = (p8est_corner_transform_t *) sc_array_index (cta,
+                                                                    ctree);
+                  nt = ct->ntree;
+                  if (ownert < nt) {
+                    continue;
+                  }
+                  p8est_quadrant_transform_corner (&r, (int) ct->ncorner, 1);
+                  if (nt < ownert || ((nt == ownert) &&
+                                      p8est_quadrant_compare (&r,
+                                                              &ownerq) < 0)) {
+                    ownerq = r;
+                    ownert = nt;
+                    ownerc = (int) ct->ncorner;
+                  }
+                }
+                sc_array_reset (cta);
+              }
+              P4EST_ASSERT (ownerq.level == P8EST_QMAXLEVEL);
+              shift = P8EST_QUADRANT_LEN (P8EST_QMAXLEVEL);
+              points[lz].x = (tick_t) (ownerq.x + ((ownerc & 1) ? shift : 0));
+              points[lz].y = (tick_t) (ownerq.y +
+                                       (((ownerc >> 1) & 1) ? shift : 0));
+              points[lz].z =
+                (tick_t) (ownerq.z + ((ownerc >> 2) ? shift : 0));
+              pids[lz] = (trilinear_mesh_pid_t) ownert;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+static unsigned
+p8est_mesh_indep_hash_fn (const void *v, const void *u)
+{
+  const p4est_locidx_t *indep = (p4est_locidx_t *) v;
+  uint32_t            a, b, c;
+
+  a = (uint32_t) indep[0];
+  b = (uint32_t) indep[1];
+  c = (uint32_t) indep[2];
+  sc_hash_mix (a, b, c);
+  a += (uint32_t) indep[3];
+  sc_hash_final (a, b, c);
+
+  return (unsigned) c;
+}
+
+static int
+p8est_mesh_indep_equal_fn (const void *v1, const void *v2, const void *u)
+{
+  const p4est_locidx_t *a = (p4est_locidx_t *) v1;
+  const p4est_locidx_t *b = (p4est_locidx_t *) v2;
+  return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
+}
+
+static              p4est_locidx_t
+p8est_mesh_dcount (p4est_lnodes_t * nodes, p4est_locidx_t ** Local_nodes,
+                   sc_hash_array_t ** Hash)
+{
+  p4est_locidx_t      nel = nodes->num_local_elements;
+  p4est_locidx_t      nin = nodes->num_local_nodes;
+  p4est_locidx_t      nln = nel * 8;
+  sc_array_t          local_nodes;
+  p4est_locidx_t      elid, nid;
+  int                 i, j, f, e;
+  p8est_lnodes_code_t *face_code = nodes->face_code;
+  int8_t              ntype[8];
+  int                 faces[6], edges[12];
+  p4est_locidx_t      indep[4], *r;
+  sc_hash_array_t    *hanging;
+  size_t              position;
+  p4est_locidx_t      num_nodes = nin;
+  p4est_locidx_t     *lp;
+
+  sc_array_init (&local_nodes, sizeof (p4est_locidx_t));
+  sc_array_resize (&local_nodes, nln);
+  memcpy (local_nodes.array, nodes->element_nodes,
+          nln * sizeof (p4est_locidx_t));
+
+  *Hash = hanging = sc_hash_array_new (4 * sizeof (p4est_locidx_t),
+                                       p8est_mesh_indep_hash_fn,
+                                       p8est_mesh_indep_equal_fn, NULL);
+
+  for (nid = 0, elid = 0; elid < nel; elid++) {
+    if (p8est_lnodes_decode (face_code[elid], faces, edges)) {
+      p8est_mesh_fcode_to_ntype (face_code[elid], ntype);
+      for (i = 0; i < 8; i++, nid++) {
+        if (ntype[i] == 1) {
+          for (j = 0; j < 3; j++) {
+            f = p8est_corner_faces[i][j];
+            if (faces[f] >= 0) {
+              P4EST_ASSERT ((faces[f] ^ p8est_corner_face_corners[i][f]) ==
+                            3);
+              break;
+            }
+          }
+          P4EST_ASSERT (j < 3);
+          for (j = 0; j < 4; j++) {
+            indep[j] =
+              nodes->element_nodes[elid * 8 + p8est_face_corners[f][j]];
+          }
+          qsort (indep, 4, sizeof (p4est_locidx_t), p4est_locidx_compare);
+          lp = (p4est_locidx_t *) sc_array_index (&local_nodes, nid);
+          r = (p4est_locidx_t *) sc_hash_array_insert_unique (hanging, indep,
+                                                              &position);
+          if (r != NULL) {
+            r[0] = indep[0], r[1] = indep[1], r[2] = indep[2], r[3] =
+              indep[3];
+            *lp = num_nodes++;
+          }
+          else {
+            *lp = nin + position;
+          }
+        }
+        else if (ntype[i] == 2) {
+          for (j = 0; j < 3; j++) {
+            e = p8est_corner_edges[i][j];
+            if (edges[e] >= 0 && edges[e] < 4) {
+              indep[0] =
+                nodes->element_nodes[elid * 8 +
+                                     p8est_edge_corners[e][(edges[e] % 2)]];
+              indep[1] = nodes->element_nodes[elid * 8 + i];
+              break;
+            }
+          }
+          P4EST_ASSERT (j < 3);
+          indep[2] = -1;
+          indep[3] = -1;
+          qsort (indep, 4, sizeof (p4est_locidx_t), p4est_locidx_compare);
+          lp = (p4est_locidx_t *) sc_array_index (&local_nodes, nid);
+          r = (p4est_locidx_t *) sc_hash_array_insert_unique (hanging, indep,
+                                                              &position);
+          if (r != NULL) {
+            P4EST_ASSERT (position == (size_t) (num_nodes - nin));
+            r[0] = indep[0], r[1] = indep[1], r[2] = indep[2], r[3] =
+              indep[3];
+            *lp = num_nodes++;
+          }
+          else {
+            *lp = nin + position;
+          }
+        }
+      }
+    }
+    else {
+      nid += 8;
+    }
+  }
+
+  *Local_nodes = (p4est_locidx_t *) local_nodes.array;
+  return num_nodes;
+}
+
+trilinear_mesh_t   *
+p8est_trilinear_mesh_new_from_lnodes (p4est_t * p4est, p4est_lnodes_t * nodes)
+{
+  const int           num_procs = p4est->mpisize;
+  const int           rank = p4est->mpirank;
+  int                 mpiret;
+  int                 k;
+  size_t              current, zz, zy;
+  int32_t             e, n;
+  int64_t             global_borrowed, global_shared;
+  int64_t             local_counts[5], global_counts[5];
+  int32link_t        *lynk, **tail;
+  p4est_topidx_t      which_tree;
+  p4est_locidx_t     *local_nodes;
+  p4est_locidx_t      num_local_nodes;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *q;
+  trilinear_elem_t   *elem;
+  trilinear_anode_t  *anode;
+  trilinear_dnode_t  *dnode;
+  trilinear_mesh_t   *mesh;
+  sc_array_t         *sharers = nodes->sharers;
+  size_t              scount = sharers->elem_count, count;
+  p4est_locidx_t      num_owned_shared = 0;
+  p8est_lnodes_rank_t *lrank;
+  point_t            *points;
+  trilinear_mesh_pid_t *pids;
+  p4est_locidx_t      nid;
+  sc_hash_array_t    *hanging;
+  p4est_locidx_t     *indep, indep_count;
+
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Into trilinear_mesh_extract with %lld total elements\n",
+     (long long) p4est->global_num_quadrants);
+
+  /* Allocate output data structure. */
+  mesh = P4EST_ALLOC_ZERO (trilinear_mesh_t, 1);
+  memset (mesh, -1, sizeof (*mesh));
+
+  /* Count dnodes */
+  num_local_nodes = p8est_mesh_dcount (nodes, &local_nodes, &hanging);
+  indep = (p4est_locidx_t *) hanging->a.array;
+  points = P4EST_ALLOC (point_t, num_local_nodes);
+  pids = P4EST_ALLOC (trilinear_mesh_pid_t, num_local_nodes);
+  p8est_mesh_points (points, pids, local_nodes, num_local_nodes, nodes,
+                     p4est);
+
+  /* Get number of owned shared. */
+  for (zz = 0; zz < scount; zz++) {
+    lrank = (p8est_lnodes_rank_t *) sc_array_index (sharers, zz);
+    if (lrank->rank == rank) {
+      num_owned_shared = lrank->shared_mine_count;
+      break;
+    }
+  }
+
+  /* Assign local counts. */
+  P4EST_ASSERT (nodes->num_local_elements == p4est->local_num_quadrants);
+  mesh->local_elem_num = p4est->local_num_quadrants;
+  mesh->local_anode_num = nodes->num_local_nodes;
+  mesh->local_dnode_num = num_local_nodes - nodes->num_local_nodes;
+  mesh->local_onode_num = nodes->owned_count;
+  mesh->local_owned_offset = 0;
+  mesh->local_node_num = num_local_nodes;
+
+  /* Communicate global counts. */
+  local_counts[0] = mesh->local_elem_num;
+  local_counts[1] = mesh->local_anode_num;
+  local_counts[2] = mesh->local_onode_num;
+  local_counts[3] = mesh->local_dnode_num;
+  local_counts[4] = num_owned_shared;
+  mpiret = sc_MPI_Allreduce (local_counts, global_counts, 5,
+                             sc_MPI_LONG_LONG_INT, sc_MPI_SUM,
+                             p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+  P4EST_ASSERT (global_counts[0] == p4est->global_num_quadrants);
+  mesh->total_elem_num = global_counts[0];
+  global_borrowed = global_counts[1] - global_counts[2];
+  mesh->total_anode_num = global_counts[2];
+  mesh->total_dnode_num = global_counts[3];
+  global_shared = global_counts[4];
+  mesh->total_node_num = mesh->total_anode_num + mesh->total_dnode_num;
+
+  /* Allocate the mesh memory. */
+  mesh->elem_table = P4EST_ALLOC (trilinear_elem_t, mesh->local_elem_num);
+  mesh->node_table = P4EST_ALLOC (trilinear_node_t, mesh->local_node_num);
+  mesh->fvnid_count_table = P4EST_ALLOC (int64_t, num_procs + 1);
+  mesh->fvnid_interval_table = P4EST_ALLOC (int64_t, num_procs + 1);
+  mesh->all_fvnid_start = mesh->fvnid_interval_table;
+  mesh->sharer_pool = sc_mempool_new (sizeof (int32link_t));
+  mesh->elem_pids = P4EST_ALLOC (trilinear_mesh_pid_t, mesh->local_elem_num);
+  mesh->node_pids = P4EST_ALLOC (trilinear_mesh_pid_t, mesh->local_node_num);
+
+  /* Assign global free variable information. */
+  mesh->fvnid_interval_table[0] = 0;
+  for (k = 0; k < num_procs; ++k) {
+    mesh->fvnid_count_table[k] = nodes->global_owned_count[k];
+    mesh->fvnid_interval_table[k + 1] = mesh->fvnid_interval_table[k] +
+      mesh->fvnid_count_table[k];
+  }
+  mesh->fvnid_count_table[num_procs] = -1;
+  mesh->global_fvnid_num = mesh->fvnid_interval_table[num_procs];
+  mesh->global_fvnid_start = 0;
+  mesh->global_fvnid_end = mesh->global_fvnid_num - 1;
+  P4EST_ASSERT (mesh->global_fvnid_num == mesh->total_anode_num);
+
+  /* Assign element information. */
+  which_tree = p4est->first_local_tree;
+  nid = 0;
+  if (which_tree >= 0) {
+    tree = p4est_tree_array_index (p4est->trees, which_tree);
+    current = 0;
+    for (e = 0; e < mesh->local_elem_num; ++e) {
+      if (current == tree->quadrants.elem_count) {
+        ++which_tree;
+        tree = p4est_tree_array_index (p4est->trees, which_tree);
+        current = 0;
+      }
+      q = p8est_quadrant_array_index (&tree->quadrants, current);
+      elem = mesh->elem_table + e;
+      for (k = 0; k < P4EST_CHILDREN; ++k) {
+        elem->local_node_id[k] = local_nodes[nid++];
+      }
+      elem->lx = (tick_t) q->x;
+      elem->ly = (tick_t) q->y;
+      elem->lz = (tick_t) q->z;
+      elem->size = P4EST_QUADRANT_LEN (q->level);
+      elem->data = q->p.user_data;
+      mesh->elem_pids[e] = (trilinear_mesh_pid_t) which_tree;
+      ++current;
+    }
+    P4EST_ASSERT (which_tree == p4est->last_local_tree);
+    P4EST_ASSERT (current == tree->quadrants.elem_count);
+  }
+
+  /* Assign anchored node information. */
+  mesh->anode_table = mesh->node_table;
+  mesh->onode_table = mesh->node_table + mesh->local_owned_offset;
+  mesh->dnode_table = mesh->node_table + mesh->local_anode_num;
+  for (n = 0; n < mesh->local_anode_num; ++n) {
+    anode = &mesh->node_table[n].anchored;
+    anode->point.x = points[n].x;
+    anode->point.y = points[n].y;
+    anode->point.z = points[n].z;
+    mesh->node_pids[n] = pids[n];
+    P4EST_ASSERT (pids[n] >= 0
+                  && (size_t) pids[n] < p4est->trees->elem_count);
+    anode->fvnid = p4est_lnodes_global_index (nodes, n);
+    anode->share = NULL;
+  }
+
+  for (zz = 0; zz < scount; zz++) {
+    lrank = (p8est_lnodes_rank_t *) sc_array_index (sharers, zz);
+    count = lrank->shared_nodes.elem_count;
+    if (lrank->rank == rank) {
+      continue;
+    }
+    for (zy = 0; zy < count; zy++) {
+      nid = *((p4est_locidx_t *) sc_array_index (&lrank->shared_nodes, zy));
+      anode = &mesh->node_table[nid].anchored;
+      tail = &anode->share;
+      while (*tail != NULL) {
+        tail = &((*tail)->next);
+      }
+      *tail = lynk = (int32link_t *) sc_mempool_alloc (mesh->sharer_pool);
+      lynk->id = (int32_t) lrank->rank;
+      lynk->next = NULL;
+    }
+  }
+
+  /* Assign hanging node information. */
+  indep_count = 0;
+  for (; n < num_local_nodes; n++) {
+    dnode = &mesh->node_table[n].dangling;
+    dnode->point.x = points[n].x;
+    dnode->point.y = points[n].y;
+    dnode->point.z = points[n].z;
+    mesh->node_pids[n] = pids[n];
+    P4EST_ASSERT (pids[n] >= 0
+                  && (size_t) pids[n] < p4est->trees->elem_count);
+    dnode->type = 0;            /* Not used in Rhea. */
+    if (indep[indep_count] == -1) {
+      dnode->local_anode_id[2] = indep[indep_count++];
+      P4EST_ASSERT (indep[indep_count] == -1);
+      dnode->local_anode_id[3] = indep[indep_count++];
+      dnode->local_anode_id[0] = indep[indep_count++];
+      dnode->local_anode_id[1] = indep[indep_count++];
+    }
+    else {
+      dnode->local_anode_id[0] = indep[indep_count++];
+      dnode->local_anode_id[1] = indep[indep_count++];
+      dnode->local_anode_id[2] = indep[indep_count++];
+      dnode->local_anode_id[3] = indep[indep_count++];
+    }
+  }
+
+  /* Assign the remaining variables. */
+  mesh->mpicomm = p4est->mpicomm;
+  mesh->mpisize = (int32_t) num_procs;
+  mesh->mpirank = (int32_t) rank;
+  mesh->recsize = (int32_t) p4est->data_size;
+  mesh->destructor = p8est_trilinear_mesh_destroy;
+
+  /* These members are incomplete and need to be filled later. */
+  memset (mesh->bounds, 0, 6 * sizeof (int));
+  memset (mesh->sizes, 0, 3 * sizeof (int));
+  mesh->minsize = mesh->maxsize = 0;
+  mesh->ticksize = 0.;
+  mesh->extra_info = NULL;
+  mesh->gid = -1;
+
+  /* We are done */
+  P4EST_GLOBAL_PRODUCTIONF ("Done trilinear_mesh_extract"
+                            " with %lld anodes %lld %lld\n",
+                            (long long) mesh->total_anode_num,
+                            (long long) global_borrowed,
+                            (long long) global_shared);
+
+  P4EST_FREE (points);
+  P4EST_FREE (pids);
+  SC_FREE (local_nodes);
+  sc_hash_array_destroy (hanging);
+  return mesh;
+}
diff --git a/doc/attic/p8est_trilinear_nodes.c b/doc/attic/p8est_trilinear_nodes.c
new file mode 100644
index 0000000..91706a8
--- /dev/null
+++ b/doc/attic/p8est_trilinear_nodes.c
@@ -0,0 +1,263 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include <p8est_trilinear.h>
+
+trilinear_mesh_t   *
+p8est_trilinear_mesh_new_from_nodes (p4est_t * p4est, p4est_nodes_t * nodes)
+{
+  const int           num_procs = p4est->mpisize;
+  const int           rank = p4est->mpirank;
+  int                 mpiret;
+  int                 k, owner;
+#ifdef P4EST_DEBUG
+  int                 prev_owner = 0;
+  int64_t             prev_fvnid = -1;
+#endif
+  int                *sharers;
+  size_t              current, zz, num_sharers;
+  int32_t             e, n, local_owned_end;
+  int64_t             global_borrowed, global_shared;
+  int64_t             local_counts[5], global_counts[5];
+  int32link_t        *lynk, **tail;
+  p4est_topidx_t      which_tree;
+  p4est_locidx_t     *local_node, *shared_offsets;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *q;
+  p4est_indep_t      *in;
+  p8est_hang4_t      *fh;
+  p8est_hang2_t      *eh;
+  trilinear_elem_t   *elem;
+  trilinear_anode_t  *anode;
+  trilinear_dnode_t  *dnode;
+  trilinear_mesh_t   *mesh;
+  trilinear_mesh_pid_t *elem_pids;
+  trilinear_mesh_pid_t *node_pids;
+  sc_recycle_array_t *rarr;
+
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Into trilinear_mesh_extract with %lld total elements\n",
+     (long long) p4est->global_num_quadrants);
+  p4est_log_indent_push ();
+
+  /* Allocate output data structure. */
+  mesh = P4EST_ALLOC_ZERO (trilinear_mesh_t, 1);
+  memset (mesh, -1, sizeof (*mesh));
+  shared_offsets = nodes->shared_offsets;
+
+  /* Assign local counts. */
+  P4EST_ASSERT (nodes->num_local_quadrants == p4est->local_num_quadrants);
+  mesh->local_elem_num = p4est->local_num_quadrants;
+  mesh->local_anode_num = nodes->indep_nodes.elem_count;
+  mesh->local_dnode_num =
+    nodes->face_hangings.elem_count + nodes->edge_hangings.elem_count;
+  mesh->local_onode_num = nodes->num_owned_indeps;
+  mesh->local_owned_offset = nodes->offset_owned_indeps;
+  mesh->local_node_num = mesh->local_anode_num + mesh->local_dnode_num;
+  local_owned_end = mesh->local_owned_offset + mesh->local_onode_num;
+
+  /* Communicate global counts. */
+  local_counts[0] = mesh->local_elem_num;
+  local_counts[1] = mesh->local_anode_num;
+  local_counts[2] = mesh->local_onode_num;
+  local_counts[3] = mesh->local_dnode_num;
+  local_counts[4] = nodes->num_owned_shared;
+  mpiret = sc_MPI_Allreduce (local_counts, global_counts, 5,
+                             sc_MPI_LONG_LONG_INT, sc_MPI_SUM,
+                             p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+  P4EST_ASSERT (global_counts[0] == p4est->global_num_quadrants);
+  mesh->total_elem_num = global_counts[0];
+  global_borrowed = global_counts[1] - global_counts[2];
+  mesh->total_anode_num = global_counts[2];
+  mesh->total_dnode_num = global_counts[3];
+  global_shared = global_counts[4];
+  mesh->total_node_num = mesh->total_anode_num + mesh->total_dnode_num;
+
+  /* Allocate the mesh memory. */
+  mesh->elem_table = P4EST_ALLOC (trilinear_elem_t, mesh->local_elem_num);
+  mesh->node_table = P4EST_ALLOC (trilinear_node_t, mesh->local_node_num);
+  mesh->fvnid_count_table = P4EST_ALLOC (int64_t, num_procs + 1);
+  mesh->fvnid_interval_table = P4EST_ALLOC (int64_t, num_procs + 1);
+  mesh->all_fvnid_start = mesh->fvnid_interval_table;
+  mesh->sharer_pool = sc_mempool_new (sizeof (int32link_t));
+  mesh->elem_pids = P4EST_ALLOC (trilinear_mesh_pid_t, mesh->local_node_num);
+  mesh->node_pids = P4EST_ALLOC (trilinear_mesh_pid_t, mesh->local_node_num);
+
+  /* Assign global free variable information. */
+  mesh->fvnid_interval_table[0] = 0;
+  for (k = 0; k < num_procs; ++k) {
+    mesh->fvnid_interval_table[k + 1] = mesh->fvnid_interval_table[k] +
+      (mesh->fvnid_count_table[k] = nodes->global_owned_indeps[k]);
+  }
+  mesh->fvnid_count_table[num_procs] = -1;
+  mesh->global_fvnid_num = mesh->fvnid_interval_table[num_procs];
+  mesh->global_fvnid_start = 0;
+  mesh->global_fvnid_end = mesh->global_fvnid_num - 1;
+  P4EST_ASSERT (mesh->global_fvnid_num == mesh->total_anode_num);
+
+  /* Assign element information. */
+  local_node = nodes->local_nodes;
+  which_tree = p4est->first_local_tree;
+  elem_pids = mesh->elem_pids;
+  if (which_tree >= 0) {
+    tree = p4est_tree_array_index (p4est->trees, which_tree);
+    current = 0;
+    for (e = 0; e < mesh->local_elem_num; ++e) {
+      if (current == tree->quadrants.elem_count) {
+        ++which_tree;
+        tree = p4est_tree_array_index (p4est->trees, which_tree);
+        current = 0;
+      }
+      q = p4est_quadrant_array_index (&tree->quadrants, current);
+      elem = mesh->elem_table + e;
+      for (k = 0; k < P4EST_CHILDREN; ++k) {
+        elem->local_node_id[k] = *local_node++;
+      }
+      elem->lx = (tick_t) q->x;
+      elem->ly = (tick_t) q->y;
+      elem->lz = (tick_t) q->z;
+      elem->size = P4EST_QUADRANT_LEN (q->level);
+      elem->data = q->p.user_data;
+      elem_pids[e] = (trilinear_mesh_pid_t) which_tree;
+      ++current;
+    }
+    P4EST_ASSERT (which_tree == p4est->last_local_tree);
+    P4EST_ASSERT (current == tree->quadrants.elem_count);
+  }
+
+  /* Assign anchored node information. */
+  mesh->anode_table = mesh->node_table;
+  mesh->onode_table = mesh->node_table + mesh->local_owned_offset;
+  mesh->dnode_table = mesh->node_table + mesh->local_anode_num;
+  node_pids = mesh->node_pids;
+  for (n = 0; n < mesh->local_anode_num; ++n) {
+    anode = &mesh->node_table[n].anchored;
+    in = (p4est_indep_t *) sc_array_index (&nodes->indep_nodes, (size_t) n);
+    anode->point.x = in->x;
+    anode->point.y = in->y;
+    anode->point.z = in->z;
+    node_pids[n] = (trilinear_mesh_pid_t) in->p.piggy3.which_tree;
+    if (n < mesh->local_owned_offset) {
+      owner = nodes->nonlocal_ranks[n];
+      P4EST_ASSERT (owner < rank && owner >= prev_owner);
+    }
+    else if (n >= local_owned_end) {
+      owner = nodes->nonlocal_ranks[n - mesh->local_onode_num];
+      P4EST_ASSERT (owner > rank && owner >= prev_owner);
+    }
+    else {
+      owner = rank;
+    }
+    anode->fvnid = mesh->all_fvnid_start[owner] + in->p.piggy3.local_num;
+    P4EST_ASSERT (anode->fvnid > prev_fvnid);
+    if (in->pad8 == 0) {
+      P4EST_ASSERT (in->pad16 == -1);
+      P4EST_ASSERT (shared_offsets == NULL || shared_offsets[n] == -1);
+      anode->share = NULL;
+    }
+    else {
+      P4EST_ASSERT (in->pad8 > 0);
+      num_sharers = (size_t) in->pad8;
+      rarr =
+        (sc_recycle_array_t *) sc_array_index (&nodes->shared_indeps,
+                                               num_sharers - 1);
+      if (nodes->shared_offsets == NULL) {
+        P4EST_ASSERT (in->pad16 >= 0);
+        zz = (size_t) in->pad16;
+      }
+      else {
+        P4EST_ASSERT (in->pad16 == -1);
+        zz = (size_t) shared_offsets[n];
+      }
+      sharers = (int *) sc_array_index (&rarr->a, zz);
+      tail = &anode->share;
+      for (zz = 0; zz < num_sharers; ++zz) {
+        *tail = lynk = (int32link_t *) sc_mempool_alloc (mesh->sharer_pool);
+        lynk->id = (int32_t) sharers[zz];
+        tail = &lynk->next;
+      }
+      *tail = NULL;
+    }
+#ifdef P4EST_DEBUG
+    prev_owner = owner;
+    prev_fvnid = anode->fvnid;
+#endif
+  }
+
+  /* Assign face hanging node information. */
+  for (zz = 0; zz < nodes->face_hangings.elem_count; ++n, ++zz) {
+    dnode = &mesh->node_table[n].dangling;
+    fh = (p8est_hang4_t *) sc_array_index (&nodes->face_hangings, zz);
+    dnode->point.x = fh->x;
+    dnode->point.y = fh->y;
+    dnode->point.z = fh->z;
+    dnode->type = 0;            /* Not used in Rhea. */
+    dnode->local_anode_id[0] = fh->p.piggy.depends[0];
+    dnode->local_anode_id[1] = fh->p.piggy.depends[1];
+    dnode->local_anode_id[2] = fh->p.piggy.depends[2];
+    dnode->local_anode_id[3] = fh->p.piggy.depends[3];
+    node_pids[n] = (trilinear_mesh_pid_t) fh->p.piggy.which_tree;
+  }
+
+  /* Assign edge hanging node information. */
+  for (zz = 0; zz < nodes->edge_hangings.elem_count; ++n, ++zz) {
+    dnode = &mesh->node_table[n].dangling;
+    eh = (p8est_hang2_t *) sc_array_index (&nodes->edge_hangings, zz);
+    dnode->point.x = eh->x;
+    dnode->point.y = eh->y;
+    dnode->point.z = eh->z;
+    dnode->type = 0;            /* Not used in Rhea. */
+    dnode->local_anode_id[0] = eh->p.piggy.depends[0];
+    dnode->local_anode_id[1] = eh->p.piggy.depends[1];
+    dnode->local_anode_id[2] = dnode->local_anode_id[3] = -1;
+    node_pids[n] = (trilinear_mesh_pid_t) eh->p.piggy.which_tree;
+  }
+  P4EST_ASSERT (n == mesh->local_node_num);
+
+  /* Assign the remaining variables. */
+  mesh->mpicomm = p4est->mpicomm;
+  mesh->mpisize = (int32_t) num_procs;
+  mesh->mpirank = (int32_t) rank;
+  mesh->recsize = (int32_t) p4est->data_size;
+  mesh->destructor = p8est_trilinear_mesh_destroy;
+
+  /* These members are incomplete and need to be filled later. */
+  memset (mesh->bounds, 0, 6 * sizeof (int));
+  memset (mesh->sizes, 0, 3 * sizeof (int));
+  mesh->minsize = mesh->maxsize = 0;
+  mesh->ticksize = 0.;
+  mesh->extra_info = NULL;
+  mesh->gid = -1;
+
+  /* We are done */
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done trilinear_mesh_extract"
+                            " with %lld anodes %lld %lld\n",
+                            (long long) mesh->total_anode_num,
+                            (long long) global_borrowed,
+                            (long long) global_shared);
+
+  return mesh;
+}
diff --git a/doc/attic/scalings.txt b/doc/attic/scalings.txt
new file mode 100644
index 0000000..8a86765
--- /dev/null
+++ b/doc/attic/scalings.txt
@@ -0,0 +1,162 @@
+All scalings are without --enable-debug unless specified otherwise.
+
+lonestar intel mvapich 278aefd55642d32305d9c6b4f6c0a0faf0502db5
+p4est_timings
+
+np	level	crc		bal	rebal
+1	11	0x334bc3de	2.28s	1.75s
+4	12	0xb0325466	5.27s	4.7s
+16	13	0x25c3b672	7.69s	7.19s
+64	14	0xad908ce4	7.88s	7.32s
+256	15	0x9e7da646	8.05s	7.56s
+1024	16	0xc0a91859	10.1s	8.98s
+
+13	13	0x25c3b672	9.47s	8.76s
+52	14	0xad908ce4	9.85s	8.99s
+208	15	0x9e7da646	10.2s	9.44s
+832	16	0xc0a91859	13s	11.5s
+
+ranger pgi openmpi 42f3eac859d71ab047a8e1da2b409187115f372e
+p4est_timings
+
+np	level	crc		bal	rebal
+1	11	0x334bc3de	3.17s	2.25s
+4	12	0xb0325466	6.22s	5.49s
+16	13	0x25c3b672	6.73s	8.11s
+64	14	0xad908ce4	9.63s	9.09s
+256	15	0x9e7da646	15.4s	9.6s
+1024	16	0xc0a91859	15.9s	9.76s
+4096	17
+
+13	13	0x25c3b672	11.6s	7.78s
+52	14	0xad908ce4	12s	11s
+208	15	0x9e7da646	12.9s	12.5s
+832	16	0xc0a91859	15.7s	12.7s
+3328	17
+
+lonestar intel mvapich 2d03e1eb0fe3b3fa12a15c0e37ba2a902ef3ff12
+p4est_timings star
+
+np      level   crc		bal	rebal
+1       10      0x61c1405a	6.6s	5.97s
+4	11	0xb1a0c369	7.84s	7s
+16	12	0xf02927bf	12.6s	11.8s
+64	13	0xc86c74d9	13s	12.2s
+256	14	0xc5a57be2	13.2s	12.5s
+1024	15	0x63801485	16s	15s
+
+ranger pgi openmpi b31dc830916870e174b25c6efff4dc1ffd547fa8
+p4est_timings unit
+
+np      level   crc             bal     rebal   part1   part2   strange
+1	11	0x334bc3de	3.19s	2.5s	0s	0.0252s
+4	12	0xb0325466	7.19s	9.39s	0.033s	0.164s
+16	13	0x25c3b672	10.3s	6.78s	0.041s	0.275s
+64	14	0xad908ce4	10.8s	10.1s	0.0873s	0.316s
+256	15	0x9e7da646	16.5s	10.7s	0.0937s	0.329s
+1024	16	0xc0a91859	17.5s	11.4s	0.109s	0.392s
+1536	16	0xc0a91859	7.65s	7.41s	0.0662s	0.264s
+2048	16	0xc0a91859	9.69s	5.42s	0.0408s	0.241s
+3072	16	0xc0a91859	3.83s	3.54s	0.0645s	9.1s   (?part2)
+4096	16	0xc0a91859	6.32s	2.65s	0.025s	0.125s (?bal)
+5389	16	0xc0a91859	6.23s	2.87s	0.0572s	0.141s (?bal)
+4096	17
+5389	17	0x2c7b8a00	16.9s	11s	0.134s	0.363s
+6144	17	0x2c7b8a00	11.5s	11.2s	0.134s	0.306s
+
+ranger pgi mvapich2
+e5daddfa1ebfc17f60024290f414e12fc9bc846c --disable-resize-realloc
+p4est_timings star (6 trees)
+
+np	level   crc		quadrants	bal	rebal	repart
+1	10	0x61c1405a	1130460		8.27s	7.44s	<1s
+4	11	0xb1a0c369	4521948		14.2s	13.3s	<1s
+16	12	0xf02927bf	18087900	15.8s	14.9s	<1s
+64	13	0xc86c74d9	72351708	16.2s	15.3s	<1s
+256	14	0xc5a57be2	289406940	16.5s	15.5s	<1s
+1024	15	0x63801485	1157627868	19.3s	16.1s	<1s
+4096	16	0x5ba3cce5	4630511580	20.5s	16.6s	<1s
+8192	16	0x5ba3cce5	4630511580	12.1s	8.11s	<1s
+6144	17	0xf111861a	18522046428	51.6s	46.7s	1.73s
+12288	17	0xf111861a	18522046428	25.5s	24.4s	<1s
+12000	18	0x946cbb40	74088185820	107s	102s	3.51s
+12288	18	0x946cbb40	74088185820	108s	106s	6.89s
+
+ranger pgi mvapich1-devel
+e5daddfa1ebfc17f60024290f414e12fc9bc846c --disable-resize-realloc
+p4est_timings star (6 trees)
+
+np	level   crc		quadrants	bal	rebal	repart
+1	10	0x61c1405a	1130460		8.26s	7.32s	<1s
+4	11	0xb1a0c369	4521948		14.2s	13.1s	<1s
+16	12	0xf02927bf	18087900	15.9s	14.9s	<1s
+64	13	0xc86c74d9	72351708	16.2s	15.2s	<1s
+256	14	0xc5a57be2	289406940	16.5s	15.4s	<1s
+1024	15	0x63801485	1157627868	18.8s	16s	<1s
+4096	16	0x5ba3cce5	4630511580	22.2s	16.2s	<1s
+8192	16	0x5ba3cce5	4630511580	10.7s	7.9s	<1s
+6144	17	0xf111861a	18522046428	68.3s	113s	1.7s	(slow!)
+8192	17	0xf111861a	18522046428	47.8s	36.8s	1.63s
+12288	17	0xf111861a	18522046428	96.5s	68.2s	19.9s	(slow!)
+12288	17	0xf111861a	18522046428	116s	104s	51s	(slow!)
+16384	17	0xf111861a	18522046428	62.8s	33.1s	<1s	(slow!)
+12000	18	0x946cbb40	74088185820	240s	191s	21.7s	(slow!)
+12288	18	0x946cbb40	74088185820	174s	147s	33.1s	(slow!)
+16384	18	0x946cbb40	74088185820	110s	95s	1.75s
+32768	18	0x946cbb40	74088185820	155s	113s	<1s
+
+ranger pgi mvapich-devel/1.0 2008-07-14
+b507affe8093d0f2a6e9558fda7a07cfc7c76bc1
+
+p4est_timings star (6 trees)
+np	level   crc             quadrants       bal     rebal   repart
+1	10	0x61c1405a	1130460		6.95s	6.06s	<1s
+4	11	0xb1a0c369	4521948		12.8s	11.7s	<1s
+16	12	0xf02927bf	18087900	14.1s	12.9s	<1s
+64	13	0xc86c74d9	72351708	14.3s	13.2s	<1s
+256	14	0xc5a57be2	289406940	14.8s	13.3s	<1s
+1024	15	0x63801485	1157627868	15s	13.5s	<1s
+4096	16	0x5ba3cce5	4630511580	18.9s	13.7s	<1s
+16384	17	0xf111861a	18522046428	59.4s	37.9s	<1s
+16384	17	0xf111861a	18522046428	41.4s	31.4s	<1s
+16384	18	0x946cbb40	74088185820	106s	94.3s	3.68s
+16384	18	0x946cbb40	74088185820	103s	88s	5.49s
+
+p8est_timings rot (6 trees)
+np	level   crc             quadrants       bal     rebal   repart
+1	7	0x885d9c4	1931586		11.5s	9.4s	<1s
+8	8	0xf7eefcea	15569154	22.6s	19.2s	<1s
+64	9	0x715df906	125000322	27.2s	23.3s	<1s
+512	10	0x535075e5	1001756034	29.5s	25.9s	<1s
+4096	11	0x4e416393	8020994946	31.8s	26.7s	1.06s
+16384	11	0x4e416393	8020994946	35.9s	36.1s	<1s
+16384	12	0x65b86a41	64195614594	105s	80.3s	2.96s
+
+ranger pgi mvapich-devel/1.0 2008-07-30
+872d0489174fb64da3be4fda06baaeb90a01abed
+
+p4est_timings star (6 trees)
+np	level   crc             quadrants       bal     rebal   repart  p/w
+16	12	0xf02927bf	18087900	14.2s	12.9s	<1s
+64	13	0xc86c74d9	72351708	14.3s	13.1s	<1s
+256	14	0xc5a57be2	289406940	14.6s	13.2s	<1s
+1024	15	0x63801485	1157627868	14.8s	13.5s	<1s
+4096	16	0x5ba3cce5	4630511580	15s	13.7s	<1s	14/8
+16384	17	0xf111861a	18522046428	25.3s	21.5s	<1s	14/8
+
+p8est_timings unit (1 tree)
+np	level   crc             quadrants       bal     rebal   repart	p/w
+256	10	0xf084cafa	166901064	9.67s	8.35s	<1s	26/15
+2048	11	0x627f17b7	1336601288	9.87s	8.61s	<1s	26/15
+16384	12	0x63c63fe6	10698347976	23.5s	17.3s	<1s	26/15
+
+p8est_timings rotcubes (6 trees)
+np	level   crc             quadrants       bal     rebal   repart	p/w
+1	7	0x885d9c4	1931586		11.6s	9.51s	<1s	0/0
+8	8	0xf7eefcea	15569154	22.8s	19s	<1s	7/4
+64	9	0x715df906	125000322	27.6s	23.3s	<1s	26/13
+512	10	0x535075e5	1001756034	30.5s	26.4s	1.03s	38/21
+4096	11	0x4e416393	8020994946	31.3s	27.1s	<1s	45/25
+8192	11	0x4e416393	8020994946	21s	14s	<1s	45/25
+16384	11	0x4e416393	8020994946	25.6s	16.1s	<1s	45/22
+16384	12	0x65b86a41	64195614594	82.4s	66.1s	3.51s	45/22
diff --git a/doc/author_holke.txt b/doc/author_holke.txt
new file mode 100644
index 0000000..58cac67
--- /dev/null
+++ b/doc/author_holke.txt
@@ -0,0 +1 @@
+I place my contributions to p4est under the GNU GPL.  Johannes Holke (holke at ins.uni-bonn.de)
diff --git a/doc/coding_standards.txt b/doc/coding_standards.txt
new file mode 100644
index 0000000..959b670
--- /dev/null
+++ b/doc/coding_standards.txt
@@ -0,0 +1,54 @@
+
+This file describes coding standards that p4est tries to adhere to.
+
+ * Portability
+
+   The code should be safe for C++ compilers.  This means that all .h
+   files need to protect external declarations with extern "C" { ... }.
+   C++ also needs explicit casts in many places where C does not care.
+ 
+   To test this, try /relative/path/to/configure --enable-mpi \
+   CC=mpicxx CFLAGS="-Wall" CXXFLAGS="-Wall" --enable-debug and run make.
+
+ * Indentation
+
+   All .c and .h files must be indented with the p4estindent script in
+   the main directory.  It calls GNU indent with some extra options.
+   This does not apply for included third party code.
+
+   If ./p4estindent gets it wrong, use /* *INDENT-OFF* */ and -ON comments.
+
+ * Boolean variables
+
+   All boolean parameters will not be checked with (a==true) or (a==false).
+   We follow the standard C convention where (a) is used to test true
+   and (!a) is used to test false.  The bool type is not used.
+   An exception is to test for pointers like (p == NULL) or (p != NULL).
+
+ * Comments
+
+   Use doxygen style for comments whenever possible.  Comments should
+   not exceed the 79th column.
+
+ * Integer types
+
+   For the indexing of trees use the p4est_topidx_t type.
+   For processor-local indexing use the p4est_locidx_t data type.
+   For globally unique indexing of potentially large numbers use the
+   p4est_gloidx_t data type.  Quadrant and node coordinates are of type
+   p4est_qcoord_t.  The int_8 data type may be used only to
+   save storage space.  Generic integers and flags should be
+   of int type whenever possible.  Storage sizes should be size_t.
+   For printf and friends use the following conversions.
+   %lld (long long) int64_t qcoord_t topidx_t locidx_t gloidx_t ssize_t
+   %llu (unsigned long long) uint64_t size_t
+   0x%08x checksums (unsigned)
+
+ * Variable names that are not self-descriptive
+
+   Loop variables of type int can be named as usual: i, j, etc.
+   Loop variables of type size_t should be named iz, jz, etc.
+   Loop variables of type ssize_t should be named is, js, etc.
+   Loop variables of type p4est_topidx_t should be named it, jt, etc.
+   Loop variables of type p4est_locidx_t should be named il, jl, etc.
+   Loop variables of type p4est_gloidx_t should be named ig, jg, etc.
diff --git a/doc/connectivity.txt b/doc/connectivity.txt
new file mode 100644
index 0000000..9537a4e
--- /dev/null
+++ b/doc/connectivity.txt
@@ -0,0 +1,53 @@
+
+The connectivity between neighbor trees and quadrants in p4est
+==============================================================
+
+In p4est, multiple octrees are connected into a forest.  The octrees are
+connected in a conforming mesh, that is, there are no hanging or split
+faces/edges between the octrees.  In 2D, quadtrees can be freely embedded into
+3D space, so it is meaningless to refer to the orientation of a quadtree by
+itself.  In 3D, all octrees have a right-handed coordinate system.  Octree and
+quadrant coordinate systems refers to an integer spacing in [0, 2^maxlevel].
+
+Each octree is connected to neighbor trees across faces, edges, and corners
+that are oriented relative to each other.
+
+2D
+Face neighbors: The neighbor can meet with 1 out of 4 possible faces, which can
+optionally be flipped around, which makes for 8 possibilities.
+Corner neighbors: There is only one way of connecting across a corner.
+
+3D
+Face neighbors: The neighbor can meet with 1 out of 6 possible faces, which can
+be rotated in 4 different ways, which makes for 24 possibilities.
+Edge neighbors: The neighbor can meet with 1 out of 12 possible edges, which
+can optionally be flipped around, which makes for 24 possibilities.
+Corner neighbors: There is only one way of connecting across a corner.
+
+The neighbor connection between octrees is encoded in p{4,8}est_connectivity.h.
+There is a comment block that describes the encoding of the configurations.
+For each configuration, a transformation is provided that maps quadrants between
+the respective coordinate systems of neighbor octrees.  A numerical application
+could use the same encoding, or a user-defined one.
+
+All quadrants (for simplicity this denotes both 2D quadrants and 3D octants)
+inherit the coordinate system of the octree they are associated with.  Thus,
+two neighbor quadrants in the same octree always meet with the identity
+configuration.  Only when quadrants meet across an octree boundary, they need
+to be transformed depending on the configuration of the neighbor octrees.
+
+For a numerical application each octree can have its own geometry/reference
+transformation.  To encode the connectivity however, it is generally not
+necessary to differentiate between intra- and inter-octree quadrant neighbors
+since the former is a special case of the latter.  It would however require
+less memory to do so, since most quadrants are away from an octree boundary and
+meet with the identity transformation that does not require encoding.  A
+non-differentiating neighbor encoding between quadrants is exposed in the
+p{4,8}est_mesh.h file.  Currently it only contains face neighbors but this will
+be extended as necessary.  This mesh information also encodes the size
+differences between neighbor octants.  When two half-size quadrants are on the
+other side, those are within the same octree and thus share the same
+transformation.
+
+For parallel computation each quadrant is assigned to one owner processor.  The
+information in p{4,8}est_mesh.h includes the processor rank of each neighbor.
diff --git a/doc/doxygen.css b/doc/doxygen.css
new file mode 100644
index 0000000..23a5e80
--- /dev/null
+++ b/doc/doxygen.css
@@ -0,0 +1,1164 @@
+/* The standard CSS for doxygen */
+
+body, table, div, p, dl {
+/*	font: 400 14px/19px Roboto,sans-serif; */
+	font: 400 14px/19px;
+}
+
+/* @group Heading Levels */
+
+h1 {
+	font-size: 150%;
+}
+
+.title {
+	font-size: 150%;
+	font-weight: bold;
+	margin: 10px 2px;
+}
+
+h2 {
+	border-bottom: 1px solid #87CBA9;
+	color: #357B58;
+	font-size: 150%;
+	font-weight: normal;
+	margin-top: 1.75em;
+	padding-top: 8px;
+	padding-bottom: 4px;
+	width: 100%;
+}
+
+h3 {
+	font-size: 100%;
+}
+
+h1, h2, h3, h4, h5, h6 {
+	-webkit-transition: text-shadow 0.5s linear;
+	-moz-transition: text-shadow 0.5s linear;
+	-ms-transition: text-shadow 0.5s linear;
+	-o-transition: text-shadow 0.5s linear;
+	transition: text-shadow 0.5s linear;
+	margin-right: 15px;
+}
+
+h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow {
+	text-shadow: 0 0 15px cyan;
+}
+
+dt {
+	font-weight: bold;
+}
+
+div.multicol {
+	-moz-column-gap: 1em;
+	-webkit-column-gap: 1em;
+	-moz-column-count: 3;
+	-webkit-column-count: 3;
+}
+
+p.startli, p.startdd, p.starttd {
+	margin-top: 2px;
+}
+
+p.endli {
+	margin-bottom: 0px;
+}
+
+p.enddd {
+	margin-bottom: 4px;
+}
+
+p.endtd {
+	margin-bottom: 2px;
+}
+
+/* @end */
+
+caption {
+	font-weight: bold;
+}
+
+span.legend {
+        font-size: 70%;
+        text-align: center;
+}
+
+h3.version {
+        font-size: 90%;
+        text-align: center;
+}
+
+div.qindex, div.navtab{
+	background-color: #EBF6F1;
+	border: 1px solid #A3D7BD;
+	text-align: center;
+}
+
+div.qindex, div.navpath {
+	width: 100%;
+	line-height: 140%;
+}
+
+div.navtab {
+	margin-right: 15px;
+}
+
+/* @group Link Styling */
+
+a {
+	color: #3D8C64;
+	font-weight: normal;
+	text-decoration: none;
+}
+
+.contents a:visited {
+	color: #46A274;
+}
+
+a:hover {
+	text-decoration: underline;
+}
+
+a.qindex {
+	font-weight: bold;
+}
+
+a.qindexHL {
+	font-weight: bold;
+	background-color: #9CD4B8;
+	color: #ffffff;
+	border: 1px double #86CAA8;
+}
+
+.contents a.qindexHL:visited {
+        color: #ffffff;
+}
+
+a.el {
+	font-weight: bold;
+}
+
+a.elRef {
+}
+
+a.code, a.code:visited {
+	color: #4665A2; 
+}
+
+a.codeRef, a.codeRef:visited {
+	color: #4665A2; 
+}
+
+/* @end */
+
+dl.el {
+	margin-left: -1cm;
+}
+
+pre.fragment {
+        border: 1px solid #C4CFE5;
+        background-color: #FBFCFD;
+        padding: 4px 6px;
+        margin: 4px 8px 4px 2px;
+        overflow: auto;
+        word-wrap: break-word;
+        font-size:  9pt;
+        line-height: 125%;
+        font-family: monospace, fixed;
+        font-size: 105%;
+}
+
+div.fragment {
+        padding: 4px;
+        margin: 4px;
+	background-color: #FBFDFC;
+	border: 1px solid #C4E5D5;
+}
+
+div.line {
+	font-family: monospace, fixed;
+        font-size: 13px;
+	min-height: 13px;
+	line-height: 1.0;
+	text-wrap: unrestricted;
+	white-space: -moz-pre-wrap; /* Moz */
+	white-space: -pre-wrap;     /* Opera 4-6 */
+	white-space: -o-pre-wrap;   /* Opera 7 */
+	white-space: pre-wrap;      /* CSS3  */
+	word-wrap: break-word;      /* IE 5.5+ */
+	text-indent: -53px;
+	padding-left: 53px;
+	padding-bottom: 0px;
+	margin: 0px;
+	-webkit-transition-property: background-color, box-shadow;
+	-webkit-transition-duration: 0.5s;
+	-moz-transition-property: background-color, box-shadow;
+	-moz-transition-duration: 0.5s;
+	-ms-transition-property: background-color, box-shadow;
+	-ms-transition-duration: 0.5s;
+	-o-transition-property: background-color, box-shadow;
+	-o-transition-duration: 0.5s;
+	transition-property: background-color, box-shadow;
+	transition-duration: 0.5s;
+}
+
+div.line.glow {
+	background-color: cyan;
+	box-shadow: 0 0 10px cyan;
+}
+
+
+span.lineno {
+	padding-right: 4px;
+	text-align: right;
+	border-right: 2px solid #0F0;
+	background-color: #E8E8E8;
+        white-space: pre;
+}
+span.lineno a {
+	background-color: #D8D8D8;
+}
+
+span.lineno a:hover {
+	background-color: #C8C8C8;
+}
+
+div.ah {
+	background-color: black;
+	font-weight: bold;
+	color: #ffffff;
+	margin-bottom: 3px;
+	margin-top: 3px;
+	padding: 0.2em;
+	border: solid thin #333;
+	border-radius: 0.5em;
+	-webkit-border-radius: .5em;
+	-moz-border-radius: .5em;
+	box-shadow: 2px 2px 3px #999;
+	-webkit-box-shadow: 2px 2px 3px #999;
+	-moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+	background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444));
+	background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000);
+}
+
+div.groupHeader {
+	margin-left: 16px;
+	margin-top: 12px;
+	font-weight: bold;
+}
+
+div.groupText {
+	margin-left: 16px;
+	font-style: italic;
+}
+
+body {
+	background-color: white;
+	color: black;
+        margin: 0;
+}
+
+div.contents {
+	margin-top: 10px;
+	margin-left: 12px;
+	margin-right: 8px;
+}
+
+td.indexkey {
+	background-color: #EBF6F1;
+	font-weight: bold;
+	border: 1px solid #C4E5D5;
+	margin: 2px 0px 2px 0;
+	padding: 2px 10px;
+        white-space: nowrap;
+        vertical-align: top;
+}
+
+td.indexvalue {
+	background-color: #EBF6F1;
+	border: 1px solid #C4E5D5;
+	padding: 2px 10px;
+	margin: 2px 0px;
+}
+
+tr.memlist {
+	background-color: #EEF7F2;
+}
+
+p.formulaDsp {
+	text-align: center;
+}
+
+img.formulaDsp {
+	
+}
+
+img.formulaInl {
+	vertical-align: middle;
+}
+
+div.center {
+	text-align: center;
+        margin-top: 0px;
+        margin-bottom: 0px;
+        padding: 0px;
+}
+
+div.center img {
+	border: 0px;
+}
+
+address.footer {
+	text-align: right;
+	padding-right: 12px;
+}
+
+img.footer {
+	border: 0px;
+	vertical-align: middle;
+}
+
+/* @group Code Colorization */
+
+span.keyword {
+	color: #008000
+}
+
+span.keywordtype {
+	color: #604020
+}
+
+span.keywordflow {
+	color: #e08000
+}
+
+span.comment {
+	color: #800000
+}
+
+span.preprocessor {
+	color: #806020
+}
+
+span.stringliteral {
+	color: #002080
+}
+
+span.charliteral {
+	color: #008080
+}
+
+span.vhdldigit { 
+	color: #ff00ff 
+}
+
+span.vhdlchar { 
+	color: #000000 
+}
+
+span.vhdlkeyword { 
+	color: #700070 
+}
+
+span.vhdllogic { 
+	color: #ff0000 
+}
+
+blockquote {
+        background-color: #F7FBF9;
+        border-left: 2px solid #9CD4B8;
+        margin: 0 24px 0 4px;
+        padding: 0 12px 0 16px;
+}
+
+/* @end */
+
+/*
+.search {
+	color: #003399;
+	font-weight: bold;
+}
+
+form.search {
+	margin-bottom: 0px;
+	margin-top: 0px;
+}
+
+input.search {
+	font-size: 75%;
+	color: #000080;
+	font-weight: normal;
+	background-color: #e8eef2;
+}
+*/
+
+td.tiny {
+	font-size: 75%;
+}
+
+.dirtab {
+	padding: 4px;
+	border-collapse: collapse;
+	border: 1px solid #A3D7BD;
+}
+
+th.dirtab {
+	background: #EBF6F1;
+	font-weight: bold;
+}
+
+hr {
+	height: 0px;
+	border: none;
+	border-top: 1px solid #4AAA7A;
+}
+
+hr.footer {
+	height: 1px;
+}
+
+/* @group Member Descriptions */
+
+table.memberdecls {
+	border-spacing: 0px;
+	padding: 0px;
+}
+
+.memberdecls td, .fieldtable tr {
+	-webkit-transition-property: background-color, box-shadow;
+	-webkit-transition-duration: 0.5s;
+	-moz-transition-property: background-color, box-shadow;
+	-moz-transition-duration: 0.5s;
+	-ms-transition-property: background-color, box-shadow;
+	-ms-transition-duration: 0.5s;
+	-o-transition-property: background-color, box-shadow;
+	-o-transition-duration: 0.5s;
+	transition-property: background-color, box-shadow;
+	transition-duration: 0.5s;
+}
+
+.memberdecls td.glow, .fieldtable tr.glow {
+	background-color: cyan;
+	box-shadow: 0 0 15px cyan;
+}
+
+.mdescLeft, .mdescRight,
+.memItemLeft, .memItemRight,
+.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
+	background-color: #F9FCFA;
+	border: none;
+	margin: 4px;
+	padding: 1px 0 0 8px;
+}
+
+.mdescLeft, .mdescRight {
+	padding: 0px 8px 4px 8px;
+	color: #555;
+}
+
+.memItemLeft, .memItemRight, .memTemplParams {
+	border-bottom: 1px solid #DEF0E7;
+}
+
+.memItemLeft, .memTemplItemLeft {
+        white-space: nowrap;
+}
+
+.memItemRight {
+	width: 100%;
+}
+
+.memTemplParams {
+	color: #46A274;
+        white-space: nowrap;
+}
+
+/* @end */
+
+/* @group Member Details */
+
+/* Styles for detailed member documentation */
+
+.memtemplate {
+	font-size: 80%;
+	color: #46A274;
+	font-weight: normal;
+	margin-left: 9px;
+}
+
+.memnav {
+	background-color: #EBF6F1;
+	border: 1px solid #A3D7BD;
+	text-align: center;
+	margin: 2px;
+	margin-right: 15px;
+	padding: 2px;
+}
+
+.mempage {
+	width: 100%;
+}
+
+.memitem {
+	padding: 0;
+	margin-bottom: 10px;
+	margin-right: 5px;
+        -webkit-transition: box-shadow 0.5s linear;
+        -moz-transition: box-shadow 0.5s linear;
+        -ms-transition: box-shadow 0.5s linear;
+        -o-transition: box-shadow 0.5s linear;
+        transition: box-shadow 0.5s linear;
+        display: table !important;
+        width: 100%;
+}
+
+.memitem.glow {
+         box-shadow: 0 0 15px cyan;
+}
+
+.memname {
+        font-weight: bold;
+        margin-left: 6px;
+}
+
+.memname td {
+	vertical-align: bottom;
+}
+
+.memproto, dl.reflist dt {
+        border-top: 1px solid #A8D9C0;
+        border-left: 1px solid #A8D9C0;
+        border-right: 1px solid #A8D9C0;
+        padding: 6px 0px 6px 0px;
+        color: #25553D;
+        font-weight: bold;
+        text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
+        background-image:url('nav_f.png');
+        background-repeat:repeat-x;
+        background-color: #E2F2EA;
+        /* opera specific markup */
+        box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+        border-top-right-radius: 4px;
+        border-top-left-radius: 4px;
+        /* firefox specific markup */
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+        -moz-border-radius-topright: 4px;
+        -moz-border-radius-topleft: 4px;
+        /* webkit specific markup */
+        -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+        -webkit-border-top-right-radius: 4px;
+        -webkit-border-top-left-radius: 4px;
+
+}
+
+.memdoc, dl.reflist dd {
+        border-bottom: 1px solid #A8D9C0;      
+        border-left: 1px solid #A8D9C0;      
+        border-right: 1px solid #A8D9C0; 
+        padding: 6px 10px 2px 10px;
+        background-color: #FBFDFC;
+        border-top-width: 0;
+        background-image:url('nav_g.png');
+        background-repeat:repeat-x;
+        background-color: #FFFFFF;
+        /* opera specific markup */
+        border-bottom-left-radius: 4px;
+        border-bottom-right-radius: 4px;
+        box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+        /* firefox specific markup */
+        -moz-border-radius-bottomleft: 4px;
+        -moz-border-radius-bottomright: 4px;
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+        /* webkit specific markup */
+        -webkit-border-bottom-left-radius: 4px;
+        -webkit-border-bottom-right-radius: 4px;
+        -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+}
+
+dl.reflist dt {
+        padding: 5px;
+}
+
+dl.reflist dd {
+        margin: 0px 0px 10px 0px;
+        padding: 5px;
+}
+
+.paramkey {
+	text-align: right;
+}
+
+.paramtype {
+	white-space: nowrap;
+}
+
+.paramname {
+	color: #602020;
+	white-space: nowrap;
+}
+.paramname em {
+	font-style: normal;
+}
+.paramname code {
+        line-height: 14px;
+}
+
+.params, .retval, .exception, .tparams {
+        margin-left: 0px;
+        padding-left: 0px;
+}       
+
+.params .paramname, .retval .paramname {
+        font-weight: bold;
+        vertical-align: top;
+}
+        
+.params .paramtype {
+        font-style: italic;
+        vertical-align: top;
+}       
+        
+.params .paramdir {
+        font-family: "courier new",courier,monospace;
+        vertical-align: top;
+}
+
+table.mlabels {
+	border-spacing: 0px;
+}
+
+td.mlabels-left {
+	width: 100%;
+	padding: 0px;
+}
+
+td.mlabels-right {
+	vertical-align: bottom;
+	padding: 0px;
+	white-space: nowrap;
+}
+
+span.mlabels {
+        margin-left: 8px;
+}
+
+span.mlabel {
+        background-color: #72C19A;
+        border-top:1px solid #53B484;
+        border-left:1px solid #53B484;
+        border-right:1px solid #C4E5D5;
+        border-bottom:1px solid #C4E5D5;
+	text-shadow: none;
+        color: white;
+        margin-right: 4px;
+        padding: 2px 3px;
+        border-radius: 3px;
+        font-size: 7pt;
+	white-space: nowrap;
+}
+
+
+
+/* @end */
+
+/* these are for tree view when not used as main index */
+
+div.directory {
+        margin: 10px 0px;
+        border-top: 1px solid #A8B8D9;
+        border-bottom: 1px solid #A8B8D9;
+        width: 100%;
+}
+
+.directory table {
+        border-collapse:collapse;
+}
+
+.directory td {
+        margin: 0px;
+        padding: 0px;
+	vertical-align: top;
+}
+
+.directory td.entry {
+        white-space: nowrap;
+        padding-right: 6px;
+}
+
+.directory td.entry a {
+        outline:none;
+}
+
+.directory td.entry a img {
+        border: none;
+}
+
+.directory td.desc {
+        width: 100%;
+        padding-left: 6px;
+	padding-right: 6px;
+	padding-top: 3px;
+	border-left: 1px solid rgba(0,0,0,0.05);
+}
+
+.directory tr.even {
+	padding-left: 6px;
+	background-color: #F7FBF9;
+}
+
+.directory img {
+	vertical-align: -30%;
+}
+
+.directory .levels {
+        white-space: nowrap;
+        width: 100%;
+        text-align: right;
+        font-size: 9pt;
+}
+
+.directory .levels span {
+        cursor: pointer;
+        padding-left: 2px;
+        padding-right: 2px;
+	color: #3D8C64;
+}
+
+div.dynheader {
+        margin-top: 8px;
+	-webkit-touch-callout: none;
+	-webkit-user-select: none;
+	-khtml-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+
+address {
+	font-style: normal;
+	color: #2A6146;
+}
+
+table.doxtable {
+	border-collapse:collapse;
+        margin-top: 4px;
+        margin-bottom: 4px;
+}
+
+table.doxtable td, table.doxtable th {
+	border: 1px solid #2D684A;
+	padding: 3px 7px 2px;
+}
+
+table.doxtable th {
+	background-color: #377F5B;
+	color: #FFFFFF;
+	font-size: 110%;
+	padding-bottom: 4px;
+	padding-top: 5px;
+}
+
+table.fieldtable {
+        width: 100%;
+        margin-bottom: 10px;
+        border: 1px solid #A8D9C0;
+        border-spacing: 0px;
+        -moz-border-radius: 4px;
+        -webkit-border-radius: 4px;
+        border-radius: 4px;
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+        -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+        box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+}
+
+.fieldtable td, .fieldtable th {
+        padding: 3px 7px 2px;
+}
+
+.fieldtable td.fieldtype, .fieldtable td.fieldname {
+        white-space: nowrap;
+        border-right: 1px solid #A8D9C0;
+        border-bottom: 1px solid #A8D9C0;
+        vertical-align: top;
+}
+
+.fieldtable td.fielddoc {
+        border-bottom: 1px solid #A8D9C0;
+        width: 100%;
+}
+
+.fieldtable tr:last-child td {
+        border-bottom: none;
+}
+
+.fieldtable th {
+        background-image:url('nav_f.png');
+        background-repeat:repeat-x;
+        background-color: #E2F2EA;
+        font-size: 90%;
+        color: #25553D;
+        padding-bottom: 4px;
+        padding-top: 5px;
+        text-align:left;
+        -moz-border-radius-topleft: 4px;
+        -moz-border-radius-topright: 4px;
+        -webkit-border-top-left-radius: 4px;
+        -webkit-border-top-right-radius: 4px;
+        border-top-left-radius: 4px;
+        border-top-right-radius: 4px;
+        border-bottom: 1px solid #A8D9C0;
+}
+
+
+.tabsearch {
+	top: 0px;
+	left: 10px;
+	height: 36px;
+	background-image: url('tab_b.png');
+	z-index: 101;
+	overflow: hidden;
+	font-size: 13px;
+}
+
+.navpath ul
+{
+	font-size: 11px;
+	background-image:url('tab_b.png');
+	background-repeat:repeat-x;
+	height:30px;
+	line-height:30px;
+	color:#8ACCAB;
+	border:solid 1px #C2E4D3;
+	overflow:hidden;
+	margin:0px;
+	padding:0px;
+}
+
+.navpath li
+{
+	list-style-type:none;
+	float:left;
+	padding-left:10px;
+	padding-right:15px;
+	background-image:url('bc_s.png');
+	background-repeat:no-repeat;
+	background-position:right;
+	color:#367C59;
+}
+
+.navpath li.navelem a
+{
+	height:32px;
+	display:block;
+	text-decoration: none;
+	outline: none;
+	font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif;
+}
+
+.navpath li.navelem a:hover
+{
+	color:#68BD92;
+}
+
+.navpath li.footer
+{
+        list-style-type:none;
+        float:right;
+        padding-left:10px;
+        padding-right:15px;
+        background-image:none;
+        background-repeat:no-repeat;
+        background-position:right;
+        color:#367C59;
+        font-size: 8pt;
+}
+
+
+div.summary
+{
+	float: right;
+	font-size: 8pt;
+	padding-right: 5px;
+	width: 50%;
+	text-align: right;
+}       
+
+div.summary a
+{
+	white-space: nowrap;
+}
+
+div.ingroups
+{
+	font-size: 8pt;
+	width: 50%;
+	text-align: left;
+}
+
+div.ingroups a
+{
+	white-space: nowrap;
+}
+
+div.header
+{
+        background-image:url('nav_h.png');
+        background-repeat:repeat-x;
+	background-color: #F9FCFA;
+	margin:  0px;
+	border-bottom: 1px solid #C4E5D5;
+}
+
+div.headertitle
+{
+	padding: 5px 5px 5px 10px;
+}
+
+dl
+{
+        padding: 0 0 0 10px;
+}
+
+/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */
+dl.section
+{
+	margin-left: 0px;
+	padding-left: 0px;
+}
+
+dl.note
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #D0C000;
+}
+
+dl.warning, dl.attention
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #FF0000;
+}
+
+dl.pre, dl.post, dl.invariant
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #00D000;
+}
+
+dl.deprecated
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #505050;
+}
+
+dl.todo
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #00C0E0;
+}
+
+dl.test
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #3030E0;
+}
+
+dl.bug
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #C08050;
+}
+
+dl.section dd {
+	margin-bottom: 6px;
+}
+
+
+#projectlogo
+{
+	text-align: center;
+	vertical-align: bottom;
+	border-collapse: separate;
+}
+ 
+#projectlogo img
+{ 
+	border: 0px none;
+}
+ 
+#projectname
+{
+	font: 300% Tahoma, Arial,sans-serif;
+	margin: 0px;
+	padding: 2px 0px;
+}
+    
+#projectbrief
+{
+	font: 120% Tahoma, Arial,sans-serif;
+	margin: 0px;
+	padding: 0px;
+}
+
+#projectnumber
+{
+	font: 50% Tahoma, Arial,sans-serif;
+	margin: 0px;
+	padding: 0px;
+}
+
+#titlearea
+{
+	padding: 0px;
+	margin: 0px;
+	width: 100%;
+	border-bottom: 1px solid #53B484;
+}
+
+.image
+{
+        text-align: center;
+}
+
+.dotgraph
+{
+        text-align: center;
+}
+
+.mscgraph
+{
+        text-align: center;
+}
+
+.caption
+{
+	font-weight: bold;
+}
+
+div.zoom
+{
+	border: 1px solid #90CEAF;
+}
+
+dl.citelist {
+        margin-bottom:50px;
+}
+
+dl.citelist dt {
+        color:#337554;
+        float:left;
+        font-weight:bold;
+        margin-right:10px;
+        padding:5px;
+}
+
+dl.citelist dd {
+        margin:2px 0;
+        padding:5px 0;
+}
+
+div.toc {
+        padding: 14px 25px;
+        background-color: #F4FAF7;
+        border: 1px solid #D8EEE3;
+        border-radius: 7px 7px 7px 7px;
+        float: right;
+        height: auto;
+        margin: 0 20px 10px 10px;
+        width: 200px;
+}
+
+div.toc li {
+        background: url("bdwn.png") no-repeat scroll 0 5px transparent;
+        font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif;
+        margin-top: 5px;
+        padding-left: 10px;
+        padding-top: 2px;
+}
+
+div.toc h3 {
+        font: bold 12px/1.2 Arial,FreeSans,sans-serif;
+	color: #46A274;
+        border-bottom: 0 none;
+        margin: 0;
+}
+
+div.toc ul {
+        list-style: none outside none;
+        border: medium none;
+        padding: 0px;
+}       
+
+div.toc li.level1 {
+        margin-left: 0px;
+}
+
+div.toc li.level2 {
+        margin-left: 15px;
+}
+
+div.toc li.level3 {
+        margin-left: 30px;
+}
+
+div.toc li.level4 {
+        margin-left: 45px;
+}
+
+.inherit_header {
+        font-weight: bold;
+        color: gray;
+        cursor: pointer;
+	-webkit-touch-callout: none;
+	-webkit-user-select: none;
+	-khtml-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+
+.inherit_header td {
+        padding: 6px 0px 2px 5px;
+}
+
+.inherit {
+        display: none;
+}
+
+tr.heading h2 {
+        margin-top: 12px;
+        margin-bottom: 4px;
+}
+
+ at media print
+{
+  #top { display: none; }
+  #side-nav { display: none; }
+  #nav-path { display: none; }
+  body { overflow:visible; }
+  h1, h2, h3, h4, h5, h6 { page-break-after: avoid; }
+  .summary { display: none; }
+  .memitem { page-break-inside: avoid; }
+  #doc-content
+  {
+    margin-left:0 !important;
+    height:auto !important;
+    width:auto !important;
+    overflow:inherit;
+    display:inline;
+  }
+}
+
diff --git a/doc/juqueen/setup-p4est b/doc/juqueen/setup-p4est
new file mode 100755
index 0000000..f16331c
--- /dev/null
+++ b/doc/juqueen/setup-p4est
@@ -0,0 +1,63 @@
+#! /bin/sh
+
+# This file can be used to compile and install p4est on the juqueen
+# supercomputer at the Juelich Supercomputing Center (JSC), Germany.
+#
+# That this script requires ZLIB_INC and ZLIB_LIB environment variables that
+# point to a current zlib installation, and a directory $HOME/nosave/unpack.
+#
+# Usage: <this script> <p4est-version.tar.gz>
+# The .tar.gz file can be created by calling make dist in a p4est build.
+
+FILE=`readlink -f "$1"`
+NAME=`basename "$FILE" .tar.gz`
+
+function bdie {
+	echo "$1"
+	exit 1
+}
+
+if test ! -f "$FILE" ; then
+	bdie "Please specify a p4est .tar.gz"
+fi
+
+# Check directories
+
+UNPACK="$HOME/nosave/unpack"
+BUILD="$HOME/nosave/build/$NAME"
+INSTALL="$HOME/stuff/local/$NAME"
+
+mkdir -p "$INSTALL" || bdie "Could not mkdir -p INSTALL"
+
+# Unpack
+
+cd "$UNPACK" || bdie "Could not cd into UNPACK"
+tar -xvzf "$FILE" || bdie "Could not UNPACK"
+
+# Make and install
+
+rm -rf "$BUILD"
+for TYPE in DEBUG FAST ; do
+	DIR="$BUILD/$TYPE"
+	export LDFLAGS="-L$ZLIB_LIB"
+	export CPPFLAGS="-I$ZLIB_INC"
+	CONFOPTS=
+	if test "x$TYPE" = xDEBUG ; then
+		export CFLAGS="-g -O0"
+		CONFOPTS="--enable-debug --enable-logging=SC_LP_INFO"
+		#continue
+	else
+		export CFLAGS="-O2 -qarch=qp -qtune=qp"
+		CONFOPTS=
+		#continue
+	fi
+	mkdir -p "$DIR" || bdie "Could not mkdir -p $DIR"
+	cd "$DIR" || bdie "Could not cd into $DIR"
+
+	"$UNPACK/$NAME/configure" \
+--enable-mpi --enable-mpiio --enable-p6est \
+--prefix="$INSTALL/$TYPE" --without-blas --disable-shared $CONFOPTS
+
+	echo "Make $TYPE"
+	make -j8 install >"$DIR/make.log" 2>"$DIR/make.err"
+done
diff --git a/doc/mainpage.dox b/doc/mainpage.dox
new file mode 100644
index 0000000..fea7c30
--- /dev/null
+++ b/doc/mainpage.dox
@@ -0,0 +1,74 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \mainpage The p4est parallel adaptive mesh refinement library
+ *
+ * \author Carsten Burstedde, Lucas C. Wilcox, Tobin Isaac et.al.
+ * \copyright GNU General Public License version 2
+ * (or, at your option, any later version)
+ *
+ * p4est is a software library for parallel adaptive mesh refinement (AMR).  It
+ * represents the mesh as one or more conforming hexahedra that can be
+ * individually refined as adaptive octrees, yielding a distributed
+ * non-conforming mesh.  Both 2D and 3D are supported.  p4est is intended to be
+ * compiled and linked against by numerical simulation codes that require
+ * efficient in-core, static or dynamic load balancing and (re-)adaptation of
+ * the computational mesh.
+ *
+ * The p4est provides data structures and routines for all of the following:
+ *
+ *  * creating a coarse computational mesh;
+ *  * adapting the mesh by refining, coarsening, and enforcing 2:1 conditions
+ *    between neighbors;
+ *  * partitioning a mesh between MPI processes;
+ *  * visualizing a mesh;
+ *  * communicating ghost layers and data between processes;
+ *  * converting the octree format to other static mesh formats;
+ *  * etc.
+ *
+ * More guidance on the usage of p4est can be found in
+ * [p4est-howto.pdf](http://p4est.github.io/p4est-howto.pdf),
+ * which is distributed with the
+ * [source](http://github.com/cburstedde/p4est) under the
+ * [doc/](https://github.com/cburstedde/p4est/tree/develop/doc) directory.
+ *
+ * To build the p4est library from a tar distribution, use the standard
+ * procedure of the GNU autotools.  The configure script takes the following
+ * options:
+ *
+ * * `--enable-debug`   lowers the log level for increased verbosity and
+ *                    activates the `P4EST_ASSERT` macro for consistency checks.
+ * * `--enable-mpi`     pulls in the mpi.h include file and activates the MPI
+ *                    compiler wrappers.  If this option is not given, wrappers
+ *                    for MPI routines are used instead and the code is compiled
+ *                    in serial only.
+ * * `--disable-mpiio`  may be used to avoid using `MPI_File` based calls.
+ *
+ * A typical development configure line looks as follows:
+ * > `relative/path/to/configure CFLAGS="-Wall -O0 -g" --enable-mpi --enable-debug`
+ * A typical production configure line looks as follows:
+ * > `relative/path/to/configure CFLAGS="-Wall -O2" --enable-mpi`
+ *
+ * \see http://www.p4est.org/
+ * \see http://www.gnu.org/licenses/licenses.html
+ */
diff --git a/doc/p4est-setup.sh b/doc/p4est-setup.sh
new file mode 100755
index 0000000..e53a666
--- /dev/null
+++ b/doc/p4est-setup.sh
@@ -0,0 +1,110 @@
+#! /bin/bash
+
+# This program comes with ABSOLUTELY NO WARRANTY.
+
+# unpack under current directory
+UNPACK=`pwd`
+
+# choose names for fast and debug compilation directories
+BUILD_DIR="$UNPACK/p4est-build"
+BUILD_FAST="$BUILD_DIR/FAST"
+BUILD_DEBUG="$BUILD_DIR/DEBUG"
+
+# functions
+busage () {
+        echo "Usage: `basename $0` <p4est_tar.gz_file> [<install location>]"
+}
+bdie () {
+        echo "Error: $@"
+        exit 1
+}
+
+if test "x$CFLAGS" = x && test "x$P4EST_CFLAGS_FAST" = x ; then
+        export CFLAGS_FAST="-O2"
+else
+        export CFLAGS_FAST="$CFLAGS $P4EST_CFLAGS_FAST"
+fi
+echo "CFLAGS_FAST: $CFLAGS_FAST"
+if test "x$CFLAGS" = x && test "x$P4EST_CFLAGS_DEBUG" = x ; then
+        export CFLAGS_DEBUG="-O0 -g"
+else
+        export CFLAGS_DEBUG="$CFLAGS $P4EST_CFLAGS_DEBUG"
+fi
+echo "CFLAGS_DEBUG: $CFLAGS_DEBUG"
+
+TGZ="$1"
+if test ! -f "$TGZ" ; then
+        busage
+        bdie "File not found"
+fi
+if ! (echo "$TGZ" | grep -q 'p4est.*.tar.gz') ; then
+        busage
+        bdie "File name mismatch"
+fi
+shift
+
+# choose names for fast and debug installation directories
+INSTALL_DIR="$1"
+if test "x$INSTALL_DIR" = x ; then
+        INSTALL_DIR="$UNPACK/p4est-install"
+else
+	shift
+fi
+INSTALL_FAST="$INSTALL_DIR/FAST"
+INSTALL_DEBUG="$INSTALL_DIR/DEBUG"
+
+echo
+echo "This script tries to unpack, configure and build the p4est library."
+echo "Build FAST: $BUILD_FAST"
+echo "Build DEBUG: $BUILD_DEBUG"
+echo "Install FAST: $INSTALL_FAST"
+echo "Install DEBUG: $INSTALL_DEBUG"
+echo "Checking environment: CFLAGS P4EST_CFLAGS_FAST P4EST_CFLAGS_DEBUG"
+
+# remove old versions
+if test -d "$BUILD_DIR" ; then
+        rm -rf "$BUILD_DIR"
+fi
+
+DIR=`echo "$TGZ" | sed 's/\(p4est.*\).tar.gz/\1/'`
+DIR=`basename $DIR`
+echo "Unpack directory: $UNPACK/$DIR"
+if test -d "$UNPACK/$DIR" ; then
+        echo "Source directory found (remove it to unpack anew)"
+else
+        echo -n "Unpacking... "
+        tar -xvz -f "$TGZ" -C "$UNPACK" >/dev/null
+        echo "done"
+fi
+test -f "$UNPACK/$DIR/src/p4est.h" || bdie "Main header file missing"
+test -f "$UNPACK/$DIR/configure" || bdie "Configure script missing"
+
+echo "See output in files .../config.output and .../make.output"
+echo
+echo "Build FAST version in $BUILD_FAST"
+mkdir -p "$BUILD_FAST"
+cd "$BUILD_FAST"
+"$UNPACK/$DIR/configure" --enable-mpi --enable-shared \
+        --disable-vtk-binary --without-blas --disable-mpithread \
+        --prefix="$INSTALL_FAST" CFLAGS="$CFLAGS_FAST" \
+        CPPFLAGS="-DSC_LOG_PRIORITY=SC_LP_ESSENTIAL -DP4EST_BACKWARD_DEALII" \
+        "$@" > config.output || bdie "Error in configure"
+make -C sc -j 8 > make.output || bdie "Error in make sc"
+make -j 8 >> make.output || bdie "Error in make p4est"
+make install >> make.output || bdie "Error in make install"
+echo "FAST version installed in $INSTALL_FAST"
+
+echo
+echo "Build DEBUG version in $BUILD_DEBUG"
+mkdir -p "$BUILD_DEBUG"
+cd "$BUILD_DEBUG"
+"$UNPACK/$DIR/configure" --enable-debug --enable-mpi --enable-shared \
+        --disable-vtk-binary --without-blas --disable-mpithread \
+        --prefix="$INSTALL_DEBUG" CFLAGS="$CFLAGS_DEBUG" \
+        CPPFLAGS="-DSC_LOG_PRIORITY=SC_LP_ESSENTIAL -DP4EST_BACKWARD_DEALII" \
+        "$@" > config.output || bdie "Error in configure"
+make -C sc -j 8 > make.output || bdie "Error in make sc"
+make -j 8 >> make.output || bdie "Error in make p4est"
+make install >> make.output || bdie "Error in make install"
+echo "DEBUG version installed in $INSTALL_DEBUG"
+echo
diff --git a/doc/tex/interface-howto.tex b/doc/tex/interface-howto.tex
new file mode 100644
index 0000000..1c63b29
--- /dev/null
+++ b/doc/tex/interface-howto.tex
@@ -0,0 +1,317 @@
+
+\documentclass[letterpaper,11pt]{article}
+
+\usepackage{amsmath}
+\usepackage{cite}
+\usepackage[margin=1in]{geometry}
+\usepackage{graphicx}
+\usepackage{subfigure}
+\usepackage{tikz}
+\usepackage{url}
+\usepackage{xspace}
+
+\usetikzlibrary{fit}
+\usetikzlibrary{positioning}
+\usetikzlibrary{trees}
+\input{tikzstyles}
+
+\newcommand{\dealii}{\texttt{deal.II}\xspace}
+\newcommand{\pforest}{\texttt{p4est}\xspace}
+
+\newcommand{\figref}[1]{Figure~\ref{fig:#1}}
+
+\author{Carsten Burstedde}
+\title{Brief \pforest interface schematics}
+
+\begin{document}
+
+\maketitle
+
+\begin{abstract}
+We describe the general procedure of using \pforest from application codes.
+\pforest is a software library that stores and modifies a forest-of-octrees
+refinement structure using distributed (MPI) parallelism.  It expects the
+description of the domain as a coarse mesh of conforming hexahedra.
+Non-conforming adaptive mesh refinement (AMR), coarsening, and other operations
+that modify the forest are implemented as \pforest API functions.  To inform
+the application about the refinement structure, several methods are provided
+that encode this information.
+\end{abstract}
+
+\section{Starting point}
+
+We generally separate the adaptive mesh refinement (AMR) topology from any
+numerical information.  The former is stored and modified internally by the
+\pforest software library, while an application is free to define the way it
+uses this information and arranges numerical and other data.  This document is
+intended to describe the interface by which \pforest relates mesh information
+to the application.
+
+The general, modular AMR pipeline is described in
+\cite{BursteddeGhattasStadlerEtAl08}, which is not specific to \pforest but can
+in principle be applied to any AMR provider.  The \pforest algorithms and main
+interface routines are described in \cite{BursteddeWilcoxGhattas11}.
+% \cite{IsaacBursteddeGhattas12}
+An example usage of \pforest as scalable mesh backend for the general-purpose
+finite element software \dealii is described in
+\cite{BangerthBursteddeHeisterEtAl11}.  A reference implementation of \pforest
+in \texttt{C} can be freely downloaded \cite{Burstedde10} and used and extended
+under the GNU General Public License.  This code contains commented header
+files and simple examples and use cases.  This software is best installed
+standalone into a dedicated directory, where an application code can then find
+the header and library files to compile and link against, respectively.
+
+In this document, we document the three distinct tasks to
+\begin{description}
+\item[A] create a coarse mesh (\figref{partA}),
+\item[B] modify the refinement and partition structure internal to \pforest
+  (\figref{partB}),
+\item[C] and to relate the mesh information to an application (\figref{partC}).
+\end{description}
+Unless indicated otherwise, all operations described below are understood as
+MPI collectives, that is, they are called on all processors simultaneously.
+Currently, part A needs to be performed redundantly on all processors, which is
+acceptable for up to $10^5$--$10^6$ coarse cells (octree roots).  Parts B and C
+strictly implement distributed parallelism, where runtime and per-processor
+memory are roughly proportional to the number of local elements (octree leaves)
+on a given processor, independent of the number of octrees, the total number of
+elements, or the refinement depth.  Partitioning the forest into equal numbers
+of elements per processor is a core \pforest operation and essentially free in
+terms of execution time, so we suggest to call it whenever load balance is
+desirable for subsequent operations.
+
+\begin{figure}
+\begin{center}
+\begin{tikzpicture}[node distance=3ex,
+  grow via three points = {one child at (0.5,-1.5) and
+  two children at (0.5,-1.5) and (0.5,-3.0)},
+  edge from parent path={(\tikzparentnode.south) |- (\tikzchildnode.west)}]
+
+\tikzstyle{every node}=[action, anchor=west]
+\tikzstyle{every child}=[arrseq]
+
+\node (createconn) {\texttt{c =} (choose one below)}
+child  {
+%\node (CAD) {CAD / mesh generator / \ldots}
+%  child {
+  node (connnew) { \texttt{c = p4est\_connectivity\_new (\ldots)} }
+  child {
+    node (connfill) { \parbox{5.2cm}{\texttt{c.tree\_to\_tree[\ldots]~= \ldots}\\
+                                   %\texttt{c\ldots}\\
+                                   etc.\ (populate members)}}
+  }
+}
+%\draw [arrseq] (CAD) -- (connnew);
+%\draw [arrseq] (connnew) -- (connfill);
+%\node[group, fit=(CAD) (connnew) (connfill)] (cadpipe) {};
+child [missing] {}
+child {
+  node (newunit) { \texttt{c = p4est\_connectivity\_new\_unitcube ()} }
+}
+child {
+  node (newstar) { \texttt{c = p4est\_connectivity\_new\_\ldots ()} }
+}
+child {
+  node (conndestroy) { \texttt{p4est\_connectivity\_destroy (c)} }
+  edge from parent[dashed]
+};
+
+\node[robject, xshift=12.5cm] (theconn) { \texttt{c} };
+\draw[arrdat] (createconn) -- (theconn);
+%\draw[arrdat] (newunit.east) -- (theconn);
+%\draw[arrdat] (newstar.east) -- (theconn);
+
+\node[object] at (0, -1.5-.75) (cadinput) {CAD};
+\node[object] at (0, -4.5-.75) (predef) {predef.};
+
+%\node[object, right=of theconn] (topartb) { Part B };
+%\draw[arrdat, dashed] (theconn.east) -- (topartb.west);
+
+%\draw[arrdat] (theconn) -- (conndestroy.east);
+\draw[arrdat] (cadinput) -- (connnew);
+\draw[arrdat] (cadinput) -- (connfill);
+\draw[arrdat] (predef) -- (newunit);
+\draw[arrdat] (predef) -- (newstar);
+
+\end{tikzpicture}
+\end{center}
+\caption{Part A, creating the coarse mesh connectivity.  The \pforest connectivity
+  \texttt{c} is a \texttt{C struct} that contains numbers and orientations of
+  neighboring coarse cells.  It can be created by translating CAD or mesh data
+  file formats or by using one of several predefined \pforest
+  functions.  The data format is documented in the extensive comment blocks in
+  \texttt{p4est\_connectivity.h} (2D) and \texttt{p8est\_connectivity.h} (3D);
+  see also \cite{BursteddeWilcoxGhattas11}.
+  In the following, \texttt{p4est} always refers to both 2D and 3D.
+}%
+\label{fig:partA}%
+\end{figure}%
+
+\begin{figure}
+\begin{center}
+\begin{tikzpicture}[%node distance=3ex,
+  grow via three points = {one child at (0.5,-1.5) and
+  two children at (0.5,-1.5) and (0.5,-3.0)},
+  edge from parent path={(\tikzparentnode.south) |- (\tikzchildnode.west)}]
+
+\tikzstyle{every node}=[action, anchor=west]
+\tikzstyle{every child}=[arrseq]
+
+\node (p4estnew) {\parbox{4.5cm}{\texttt{p = p4est\_new (c)}\\
+                                (modify \texttt{p} in-place below)}}
+  child {
+    node (p4estrefine) {\texttt{p4est\_refine (p)}}
+  }
+  child {
+    node (p4estcoarsen) {\texttt{p4est\_coarsen (p)}}
+  }
+  child { 
+    node (p4estbalance) {\texttt{p4est\_balance (p)}}
+  }
+  child {
+    node (p4estpartition) {\texttt{p4est\_partition (p)}}
+  }
+  child {
+    node (ghostnew) {\parbox{4.6cm}{\texttt{g = p4est\_ghost\_new (p)}\\
+                                  (does not modify \texttt{p})}}
+    child {
+      node (ghostdestroy) {\texttt{p4est\_ghost\_destroy (g)}}
+      edge from parent[dashed]
+    }
+  }
+  child [missing] {}
+  child {
+    node (p4estdestroy) {\texttt{p4est\_destroy (p)}}
+    edge from parent[dashed]
+  };
+
+\node[object, xshift=-1.5cm] (cinput) {\texttt{c}};
+\draw[arrdat] (cinput) -- (p4estnew);
+
+\node[robject, xshift=11.2cm] (poutput) {\texttt{p}};
+\draw[arrdat] (p4estnew) -- (poutput);
+\node[robject, xshift=11.2cm, yshift=-7.5cm] (goutput) {\texttt{g}};
+\draw[arrdat] (ghostnew) -- (goutput);
+
+\node[robject, xshift=11.2cm, yshift=-1.5cm-.75cm] (flags) {flags};
+\draw[arrdat] (flags) -- (p4estrefine);
+\draw[arrdat] (flags) -- (p4estcoarsen);
+
+\end{tikzpicture}
+\end{center}
+\caption{Part B, changing the refinement structure and partition.  With the
+  connectivity \texttt{c} created in part A, we can create the distributed
+  \pforest structure.  Several functions for its modification exist.  For a
+  given \pforest snapshot, we can create a ghost layer \texttt{g} of off-processor
+  leaves, which will be outdated and should be destroyed once \texttt{p} is
+  changed again.  Refinement and coarsening are controlled by callback functions
+  that usually query flags
+  determined by the application.  The \texttt{C struct}s \texttt{p} and \texttt{g}
+  can be inspected directly by an application, for example to loop
+  through data associated with local and ghost leaves.
+}%
+\label{fig:partB}%
+\end{figure}%
+
+\begin{figure}
+\begin{center}
+\begin{tikzpicture}[%node distance=3ex,
+  grow via three points = {one child at (0.5,-1.5) and
+  two children at (0.5,-1.5) and (0.5,-3.0)},
+  edge from parent path={(\tikzparentnode.south) |- (\tikzchildnode.west)}]
+
+\tikzstyle{every node}=[action, anchor=west]
+\tikzstyle{every child}=[arrseq]
+
+\node (createmesh) {%\parbox{8cm}{
+\texttt{n =} %(numbering of degrees of freedom)\\
+                                 (choose one below)}
+  child {
+    node (nodesnew) {\texttt{n = p4est\_nodes\_new (p, g)}}
+  }
+  child {
+    node (lnodesnew) {\texttt{n = p4est\_lnodes\_new (p, g)}}
+  }
+  child {
+    node (iterate) {\parbox{4cm}{\texttt{n =} (custom allocation)\\
+      \texttt{p4est\_iterate (p, g)}}}
+  }
+  child {
+    node (custom) {\texttt{n =} (custom allocation and code)}
+  }
+  child {
+    node (ndestroy) {(call appropriate destructor on \texttt{n})}
+    edge from parent[dashed]
+  }
+;
+
+\node[object, xshift=-2.0cm] (pginput) {\texttt{p, g}};
+\draw[arrdat] (pginput) -- (createmesh);
+
+\node[robject, xshift=9.0cm] (themesh) {\texttt{n}};
+\draw[arrdat] (createmesh) -- (themesh);
+
+\end{tikzpicture}
+\end{center}
+\caption{Part C, creating an application-specific numbering of degrees of freedom.
+  The \texttt{nodes} and \texttt{lnodes} constructors create a processor-local
+  view of globally unique node numbers and the dependency lists for hanging
+  nodes for continuous tensor-product piecewise linear and piecewise polynomial
+  finite elements, respectively.  
+  The iterator provides a generic way to traverse the refinement structure and to
+  have callbacks executed for every face, edge, and corner neighborhood, which can
+  then be used to identify node locations and their hanging status for any custom
+  element type.  The \texttt{C}
+  prototypes and documentation can be found in the respective \pforest
+  \texttt{.h} files.%  Custom traversal and numbering can work as well.
+}%
+\label{fig:partC}%
+\end{figure}%
+
+\begin{figure}
+\begin{center}
+  \subfigure[Generating the initial refinement structure and mesh.]{%
+  \includegraphics[width=.9\columnwidth]{plots/adaptivity/amr_octor1}%
+  }
+  \\
+  \subfigure[A-posteriori change of refinement structure and mesh.]{%
+  \includegraphics[width=.9\columnwidth]{plots/adaptivity/amr_octor2}%
+  }
+\end{center}
+\caption{Separation between \pforest on one hand and application-specific mesh
+  and numerical information on the other.
+  The dark blue nodes in the picture correspond to in-place operations on the
+  forest \texttt{p} that fall under part B (\figref{partB}).  The dark green node
+  labeled
+  ExtractMesh refers to creating a node numbering structure \texttt{n} (part C;
+  see \figref{partC}) and the allocation of numerical space in application
+  memory guided by \texttt{n}.  The bright green fields in the bottom-most row refer
+  to moving the application-specific numerical information from the old mesh to
+  the new one.  We suggest doing this in two steps: Interpolation works
+  processor-local since the partition has not been changed at this point.  Then,
+  after partitioning, transfer moves data to the new owner processors of the
+  respective degrees of freedom without changing the refinement pattern any
+  further.  Both operations require the existence of both the old and
+  new \texttt{n} structures, and allow for freeing the older one afterwards.
+  (Plots originally published in \cite{BursteddeGhattasStadlerEtAl08}.)
+}%
+\label{fig:meshes}%
+\end{figure}%
+
+The definition and organisation of numerical data is entirely left up to the
+application.  The application calls modification operations for the forest
+(part B), guided by the numerical state related to the local leaves.  A
+numerical application will require its own numbering scheme for degrees of
+freedom, which is most of the time defined via node locations on a reference
+element and dependencies between hanging nodes and the independent nodes they
+are interpolated from.  \pforest provides several paths for aiding the
+application in defining their data layout, henceforth called the mesh (part C),
+and not to be confused with the coarse octree mesh (part A).  \figref{meshes}
+contains illustrations on the sequence of operations that are required to
+create a new refinement pattern from scratch or by modifying an existing one,
+and to move the application-specific numerical data from an old to a new mesh.
+
+\bibliographystyle{siam}
+\bibliography{ccgo}
+
+\end{document}
diff --git a/doc/tex/octreemap.tex b/doc/tex/octreemap.tex
new file mode 100644
index 0000000..5de1840
--- /dev/null
+++ b/doc/tex/octreemap.tex
@@ -0,0 +1,88 @@
+
+\documentclass[letterpaper,11pt]{article}
+
+\usepackage{amsmath}
+\usepackage{amssymb}
+\usepackage[margin=1in,nohead]{geometry}
+\usepackage{tikz}
+
+\newcommand{\sR}{\mathbb{R}}
+
+\newcommand{\figlab}[1]{\label{fig:#1}}
+\newcommand{\figref}[1]{Figure~\ref{fig:#1}}
+
+\tikzstyle{coordaxis}=[draw=red!50!black, thick, ->]
+\tikzstyle{mapping}=[draw=blue!50!black, thick, ->]
+\tikzstyle{connect}=[draw=green!50!black, thick, ->]
+
+\author{Carsten Burstedde}
+\title{Documentation on octree and quadrant connectivity}
+
+\begin{document}
+
+\maketitle
+
+\section{Mappings between octrees and physical space}
+
+See \figref{octreemap} for an illustration.
+
+\begin{figure}[b!]
+\centering
+\begin{tikzpicture}
+  % first tree
+  \begin{scope}[xshift=0cm]
+  \draw (0,0) rectangle (3,3) node (m1) [midway] {$k_1$};
+  \draw [style=coordaxis] (0,0) -- (1.5,0) node [below] {$x_1$};
+  \draw [style=coordaxis] (0,0) -- (0,1.5) node [left] {$y_1$};
+  \filldraw (3,0.75) node (r1) {} circle (2pt) node [left] {$r_1$};
+  \end{scope}
+
+  % second tree
+  \begin{scope}[xshift=4.4cm]
+  \draw (0,0) rectangle (3,3) node (m2) [midway] {$k_2$};
+  \draw [style=coordaxis] (3,3) -- (3,1.5) node [right] {$x_2$};
+  \draw [style=coordaxis] (3,3) -- (1.5,3) node [above] {$y_2$};
+  \filldraw (0,0.75) node (r2) {} circle (2pt) node [right] {$r_2$};
+  \end{scope}
+
+  % physical space
+  \begin{scope}[xshift=1.2cm, yshift=4cm]
+  \draw (0,0) to [bend left=20] (5,1) node (pm1) [midway] {}
+              to [bend left=10] (6,3)
+              to [bend right=20] (-1,2) node (pm2) [midway] {}
+              to [bend right=10] (0,0);
+  \draw (pm1.center) to [bend left=5] (pm2.center) node (pmm) [pos=.25] {};
+  \filldraw (pmm) circle (2pt) node [above right] {$p \in \sR^3$};
+  \end{scope}
+
+  % connections
+  \draw [connect] (r1) to [bend right=20] (r2)
+        node [midway, below] {$T_{1,2}$};
+  \draw [connect] (r2) to [bend right=20] (r1)
+        node [midway, above] {$T_{2,1}$};
+  \draw [mapping] (r1) to [bend left=40] (pmm) node [pos=0.55, left] {$\phi_1$};
+  \draw [mapping] (r2) to [bend right=40] (pmm) node [pos=0.6, right] {$\phi_2$};
+
+\end{tikzpicture}
+\caption{%
+Two octrees $k_1$ and $k_2$ and their individual octree coordinate systems
+(red).  Quadrant coordinates at subdivision level $\ell \ge 0$ (not shown) are
+discrete numbers that are integer multiples of $2^{-\ell}$.  In this example
+the octrees connect through a common face, which defines coordinate
+transformations $T_{1,2} = T_{2,1}^{-1}$ across this face.  The octree points
+$r_1 = (1, \frac14)$ and $r_2 = (\frac34, 1)$ are identified: $T_{i,j}(r_i) =
+r_j$ (green).  This identification is specified solely based on the
+connectivity relation between the two octrees.  The functions $T_i$ can be
+implemented in integer arithmetic, entirely without using physical coordinates.
+To map the octrees to physical space, geometry transformations $\phi_1$,
+$\phi_2$ (blue) are introduced that satisfy the compatibility condition $p =
+\phi_1 (r_1) = \phi_2 (r_2)$.  This offers the alternate identification
+criterion $r_j = (\phi_j ^{-1} \circ \phi_i) (r_i)$.  However, this approach is
+not recommended since it is much more expensive and, more importantly, depends
+on the floating-point functions $\phi_i$ that suffer from roundoff error and
+make it difficult to determine uniqueness.
+}
+\figlab{octreemap}
+\end{figure}
+
+\end{document}
diff --git a/doc/tex/tikzstyles.tex b/doc/tex/tikzstyles.tex
new file mode 100644
index 0000000..78e025d
--- /dev/null
+++ b/doc/tex/tikzstyles.tex
@@ -0,0 +1,12 @@
+
+\tikzstyle{group}=[rectangle, rounded corners, thick, draw=green,
+  minimum size=5ex, inner sep=1.5ex]
+\tikzstyle{action}=[rectangle, rounded corners, thick, draw=blue,
+  minimum size=5ex, inner sep=1.5ex]
+\tikzstyle{object}=[rectangle, sharp corners, thick, draw=purple,
+  minimum size=5ex, inner sep=1.5ex]
+\tikzstyle{robject}=[object, anchor=east]
+
+
+\tikzstyle{arrseq}=[->, >=stealth, thick, draw=blue]
+\tikzstyle{arrdat}=[->, >=stealth, thick, dashed, draw=red]
diff --git a/doc/webpage/index.html b/doc/webpage/index.html
new file mode 100644
index 0000000..f48b8fe
--- /dev/null
+++ b/doc/webpage/index.html
@@ -0,0 +1,264 @@
+<html>
+<head>
+<title>p4est: Parallel Adaptive Mesh Refinement
+on Forests of Octrees</title>
+<meta name="author" content="Carsten Burstedde">
+<link type="text/css" rel="stylesheet" href="p4est.css">
+</head>
+<body>
+
+<h2><tt>p4est</tt>: Parallel AMR on Forests of Octrees</h2>
+
+<p>
+The <tt>p4est</tt> software library enables the dynamic management of
+a collection of adaptive octrees, conveniently called a forest of
+octrees.  <tt>p4est</tt> is designed to work in parallel and scales to
+hundreds of thousands of processor cores.  It is free software released
+under GNU General Public Licence version 2, or (at your option) any
+later version.
+</p>
+
+<div class="citation">
+<h3>Source code</h3>
+<p class="text">
+Please see the <a href="http://github.com/cburstedde/p4est">github repository</a>
+of <tt>p4est</tt> or download the
+<a href="http://p4est.github.io/release/p4est-1.0.tar.gz">latest release tarball</a>.
+The source comes with commented example programs and test cases.
+You can also download the old stable
+<a href="http://p4est.github.io/release/p4est-0.3.4.2.tar.gz">version 0.3.4.2</a>
+that is known to work with <a href="http://www.dealii.org/">deal.II</a>,
+or have a look at
+<a href="https://github.com/p4est/p4est.github.io/tree/master/release/">other
+official releases</a>.
+</p>
+<!--
+<p class="text">
+This is the <a href="http://github.com/cburstedde/libsc">github repository</a>
+for the <tt>sc</tt> auxiliary library.  A tarball is not necessary since <tt>sc</tt>
+is included the <tt>p4est</tt> tarball.
+</p>
+//-->
+</div>
+
+ 
+
+<div class="citation">
+<h3>Autogenerated API documentation</h3>
+<p class="text">
+This is the <a href="http://p4est.github.io/api/index.html">doxygen output</a> for <tt>p4est</tt>.
+You can recreate it with <tt>make doxygen</tt> after calling <tt>configure</tt>.
+</p>
+</div>
+
+ 
+
+<div class="citation">
+<h3>Howto document and step-by-step examples</h3>
+<p class="text">
+This as a <a href="http://p4est.github.io/p4est-howto.pdf">howto document</a> that documents the basic
+interface design of <tt>p4est</tt> and comments on the
+<a href="http://github.com/cburstedde/p4est/tree/develop/example/steps/">
+step-by-step examples</a> included in the source code.
+</p>
+</div>
+
+ 
+
+<div class="citation">
+<h3>Questions / Get involved</h3>
+<p class="text">
+We appreciate comments, questions, issues reported, or suggestions for adding features.
+Please email us at <a href="mailto:p4est at librelist.com">p4est at librelist.com</a>.
+This mailing list is <a href="http://librelist.com/browser/p4est/">archived</a>;
+see <a href="http://librelist.com/">librelist.com</a> for details.
+Your first message will auto-subscribe you and provide instructions to proceed.
+<!-- (it may be necessary to resend your initial mail). -->
+</p>
+
+ 
+
+<p class="text">
+We will also consider pull requests posted on <a href="http://www.github.com/">github</a>.
+</p>
+</div>
+
+ 
+
+<div class="citation">
+<h3>Technical papers / Citations</h3>
+<!--
+<p class="text">
+The original technical paper on <tt>p4est</tt> is
+<a href="http://p4est.github.io/papers/BursteddeWilcoxGhattas11.pdf">paper1 available as pdf</a> [1a].
+We have recently submitted a second technical paper to cover additional
+API functions;
+<a href="http://arxiv.org/abs/1406.0089">see here for paper2</a> [1b].
+Recently, the 2:1 balance algorithm has been rewritten; see
+<a href="http://p4est.github.io/papers/IsaacBursteddeGhattas12.pdf"
+>this article</a> [1c] on the new logic.
+</p>
+//-->
+
+<p>
+If you use <tt>p4est</tt> for your publications, please cite it as follows [1a].
+The reference [1b] is for people specifically using the topology iterator and/or the
+high-order node numbering.  [1c] is for people interested in the 2:1 balance
+details or the strong scaling limit.
+</p>
+<p class="cite">
+[1a]
+Carsten Burstedde, Lucas C. Wilcox, and Omar Ghattas,<br>
+<em>p4est: Scalable Algorithms for Parallel Adaptive Mesh Refinement on
+Forests of Octrees.</em><br>
+Published in SIAM Journal on Scientific Computing 33 no. 3 (2011),
+pages 1103-1133
+(<a href="http://p4est.github.io/papers/BursteddeWilcoxGhattas11.pdf">download</a>).
+</p>
+
+<pre class="bibtex">
+ at ARTICLE{BursteddeWilcoxGhattas11,
+  author = {Carsten Burstedde and Lucas C. Wilcox and Omar Ghattas},
+  title = {{\texttt{p4est}}: Scalable Algorithms for Parallel Adaptive Mesh
+           Refinement on Forests of Octrees},
+  journal = {SIAM Journal on Scientific Computing},
+  volume = {33},
+  number = {3},
+  pages = {1103-1133},
+  year = {2011},
+  doi = {10.1137/100791634}
+}
+</pre>
+ 
+<p class="cite">
+[1b]
+Tobin Isaac, Carsten Burstedde, Lucas C. Wilcox, and Omar Ghattas,<br>
+<em>Recursive algorithms for distributed forests of octrees.</em>
+Submitted
+(<a href="http://arxiv.org/abs/1406.0089">arXiv:1406.0089</a>), 2014.
+</p>
+ 
+<p class="cite">
+[1c]
+Tobin Isaac, Carsten Burstedde, and Omar Ghattas,<br>
+<em>Low-Cost Parallel Algorithms for 2:1 Octree Balance.</em><br>
+Published in Proceedings of the 26th IEEE International Parallel &
+Distributed Processing Symposium, 2012
+(<a href="http://p4est.github.io/papers/IsaacBursteddeGhattas12.pdf">download</a>).
+<br>
+<em>Errata:</em>
+In Algorithm 7, line 3 reads "for all o in R;" it should read "for all o in R
+and R^new."
+</p>
+</div>
+
+<p>
+<tt>p4est</tt> uses <tt>libsc</tt> written by the same authors and others
+for basic helper functionality such as logging, array and
+hash data structures, parallel statistics, and more.  <tt>libsc</tt>
+also integrates the third-party libraries <tt>zlib</tt> and
+<tt>lua</tt>.  <tt>libsc</tt> is
+free software under LGPL v2.1 (or later) and hosted under
+<a href="https://github.com/cburstedde/libsc">github</a> as well.
+</p>
+
+<div class="faq">
+<!-- This license FAQ is included by popular demand. -->
+<table class="faq" border="1" width="100%">
+<tr><td>May I copy and modify <tt>p4est</tt> source code for internal use?</td>
+<td>yes</td></tr>
+<tr><td>Will my source that contains copied or modified <tt>p4est</tt> code
+automatically be GPL?</td>
+<td>yes</td></tr>
+<tr><td>Will my source that includes <tt>p4est</tt> header files
+and is supposed to be linked against <tt>p4est</tt> automatically by GPL?</td>
+<td>no</td></tr>
+<tr><td>May I distribute my source that includes <tt>p4est</tt> header files
+and is supposed to be linked against <tt>p4est</tt>?</td>
+<td>yes</td></tr>
+<tr><td>May I distribute a binary executable that links against <tt>p4est</tt>
+if I distribute my source code along with it?</td>
+<td>yes</td></tr>
+<tr><td>May I distribute a binary executable that links against <tt>p4est</tt>
+without distributing my source code along with it?</td>
+<td>no, but:</td></tr>
+<tr><td>May I contact <a href="http://www.otc.utexas.edu/">UT Austin OTC</a>
+to negotiate permission to distribute a binary executable without the source?</td>
+<td>yes</td></tr>
+</table>
+</div>
+
+<p>
+The <a href="http://www.forestclaw.org/">ForestClaw</a> project is an
+ongoing collaboration with
+<a href="http://math.boisestate.edu/~calhoun/www_personal">Donna Calhoun</a>
+to solve hyperpolic PDEs.
+</p>
+
+<p>
+The generic adaptive finite element software <a
+href="http://www.dealii.org/">deal.II</a> now interfaces to
+<tt>p4est</tt> to obtain distributed mesh information [2].  The corresponding
+algorithms are described in this <a
+href="http://p4est.github.io/papers/BangerthBursteddeHeisterEtAl11.pdf"
+>article</a>.  If you use <tt>deal.II</tt> with <tt>p4est</tt> for your
+publications, please cite it as:
+</p>
+
+<div class="citation">
+<p class="cite">
+[2]
+Wolfgang Bangerth, Carsten Burstedde, Timo Heister, and Martin
+Kronbichler,<br>
+<em>Algorithms and Data Structures for Massively Parallel Generic
+Adaptive Finite Element Codes.</em><br>
+Published in ACM Transactions on Mathematical Software 38 No. 2 (2011),
+pages 14:1-14:28
+(<a href="http://p4est.github.io/papers/BangerthBursteddeHeisterEtAl11.pdf">download</a>).
+</p>
+
+<pre class="bibtex">
+ at ARTICLE{BangerthBursteddeHeisterEtAl11,
+  author = {Wolfgang Bangerth and Carsten Burstedde and Timo Heister and Martin
+	Kronbichler},
+  title = {Algorithms and Data Structures for Massively Parallel Generic Adaptive
+	Finite Element Codes},
+  journal = {ACM Transactions on Mathematical Software},
+  volume = {38},
+  number = {2},
+  pages = {14:1-14:28},
+  year = {2011}
+}
+</pre>
+</div>
+
+<p>
+The <tt>p4est</tt> authors:<br>
+<a href="http://burstedde.ins.uni-bonn.de/">Carsten Burstedde</a><br>
+<a href="http://lucaswilcox.com/">Lucas C. Wilcox</a><br>
+<a href="http://users.ices.utexas.edu/~tisaac/">Tobin Isaac</a><br>
+<!--
+Johann Rudi<br>
+Ethan Alan Hereth<br>
+Johannes Holke<br>
+Alex Fikl<br>
+Hannes Frank<br>
+//-->
+</p>
+
+<div class="citation">
+The development of <tt>p4est</tt> was partially supported by the US National
+Science Foundation (NSF Grants No. OCI-0749334, CCF-0427985, CNS-0540372,
+CNS-0619838, DMS-0724746, OPP-0941678) and the US Department of Energy (DOE
+Grants No. 06ER25782, 08ER25860, SC0002710).  The authors thank the Texas
+Advanced Computing Center (TACC) for providing them with access to the Ranger
+supercomputer under NSF TeraGrid award MCA04N026, and the National Center for
+Computational Science (NCCS) for early-user access to the Jaguar Cray XT5
+supercomputer.  Any opinions, findings and conclusions or recomendations
+expressed on this web page or in the source code and documentation are those of
+the authors and do not necessarily reflect the views of the National Science
+Foundation (NSF).
+</div>
+
+</body>
+</html>
diff --git a/doc/webpage/p4est.css b/doc/webpage/p4est.css
new file mode 100644
index 0000000..bb768a6
--- /dev/null
+++ b/doc/webpage/p4est.css
@@ -0,0 +1,66 @@
+body {
+	color: #000000;
+	background: #FFFFFF;
+	font-family: sans-serif;
+	margin: 1em 10% 1em 10%;
+}
+
+tt {
+        color: #006633;
+        font-weight: bold;
+}
+
+hr {
+        margin-top: 1em;
+        margin-bottom: 1em;
+}
+
+h3 {
+	margin-top: 0em;
+}
+
+p.text {
+        margin-top: 0;
+        margin-bottom: 0;
+}
+
+p.cite {
+        text-indent: -1em;
+        margin-left: 1em;
+        margin-top: 0;
+        margin-bottom: 0;
+}
+
+pre.bibtex {
+        margin-bottom: 0;
+}
+
+div.citation {
+        background-color: #E0F0E8;
+        border-style: dashed;
+        border-width: thin;
+        padding: 1ex;
+}
+
+table.text {
+	border-style: solid;
+	border-collapse: collapse;
+	border-width: 1px;
+}
+table.text td {
+	padding: .5em;
+}
+
+table.faq {
+        background-color: #E0F0E8;
+	border-style: solid;
+	border-collapse: collapse;
+	border-width: 0px;
+}
+
+div.faq {
+        background-color: #FFFCC9;
+        border-style: dashed;
+        border-width: thin;
+        padding: 1ex;
+}
diff --git a/example/balance_seeds/Makefile.am b/example/balance_seeds/Makefile.am
new file mode 100644
index 0000000..62bd03a
--- /dev/null
+++ b/example/balance_seeds/Makefile.am
@@ -0,0 +1,24 @@
+
+# This file is part of p4est.
+# Makefile.am in example/timings
+# included non-recursively from toplevel directory
+
+if P4EST_ENABLE_BUILD_2D
+bin_PROGRAMS += example/balance_seeds/p4est_balance_seeds
+
+example_balance_seeds_p4est_balance_seeds_SOURCES = \
+        example/balance_seeds/balance_seeds2.c
+
+LINT_CSOURCES += \
+        $(example_balance_seeds_p4est_balance_seeds_SOURCES)
+endif
+
+if P4EST_ENABLE_BUILD_3D
+bin_PROGRAMS += example/balance_seeds/p8est_balance_seeds
+
+example_balance_seeds_p8est_balance_seeds_SOURCES = \
+        example/balance_seeds/balance_seeds3.c
+
+LINT_CSOURCES += \
+        $(example_balance_seeds_p8est_balance_seeds_SOURCES)
+endif
diff --git a/example/balance_seeds/balance_seeds2.c b/example/balance_seeds/balance_seeds2.c
new file mode 100644
index 0000000..7489c7f
--- /dev/null
+++ b/example/balance_seeds/balance_seeds2.c
@@ -0,0 +1,185 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2011 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <sc_dmatrix.h>
+#ifndef P4_TO_P8
+#include <p4est_balance.h>
+#include <p4est_bits.h>
+#include <p4est_vtk.h>
+#include <p4est_extended.h>
+#else
+#include <p8est_balance.h>
+#include <p8est_bits.h>
+#include <p8est_vtk.h>
+#include <p8est_extended.h>
+#endif
+
+typedef struct
+{
+  int                 flag;
+}
+balance_seeds_elem_t;
+
+#ifndef P4_TO_P8
+static const int    refine_level = 8;
+static p4est_quadrant_t center = { 0x10000000, 0x10000000, 2, 0, 0, {NULL} };
+#else
+static const int    refine_level = 8;
+static p4est_quadrant_t center =
+  { 0x20000, 0x20000, 0x20000, 2, 0, 0, {NULL} };
+#endif
+
+static void
+init_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+         p4est_quadrant_t * quadrant)
+{
+  ((balance_seeds_elem_t *) (quadrant->p.user_data))->flag = -2;
+}
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+#ifndef P4_TO_P8
+  p4est_connect_type_t balance = P4EST_CONNECT_FACE;
+#else
+  p4est_connect_type_t balance = P8EST_CONNECT_EDGE;
+#endif
+  p4est_quadrant_t    desc;
+  int                 i;
+
+  if (((balance_seeds_elem_t *) (quadrant->p.user_data))->flag > -2) {
+    return 0;
+  }
+
+  if (p4est_quadrant_is_ancestor (quadrant, &center)) {
+    return 1;
+  }
+  if (p4est_quadrant_is_equal (quadrant, &center)) {
+    ((balance_seeds_elem_t *) (quadrant->p.user_data))->flag = center.level;
+    return 0;
+  }
+#ifndef P4_TO_P8
+  if (quadrant->x >= 0x30000000 || quadrant->y >= 0x30000000) {
+    ((balance_seeds_elem_t *) (quadrant->p.user_data))->flag = -1;
+    return 0;
+  }
+#else
+  if (quadrant->x >= 0x60000 || quadrant->y >= 0x60000 ||
+      quadrant->z >= 0x60000) {
+    ((balance_seeds_elem_t *) (quadrant->p.user_data))->flag = -1;
+    return 0;
+  }
+#endif
+
+  for (i = 0; i < P4EST_CHILDREN; i++) {
+    p4est_quadrant_corner_descendant (quadrant, &desc, i, P4EST_QMAXLEVEL);
+    if (p4est_balance_seeds (&desc, &center, balance, NULL)) {
+      break;
+    }
+  }
+  if (i == P4EST_CHILDREN) {
+    P4EST_ASSERT (!p4est_balance_seeds (quadrant, &center, balance, NULL));
+    ((balance_seeds_elem_t *) (quadrant->p.user_data))->flag = -1;
+    return 0;
+  }
+  p4est_quadrant_corner_descendant (quadrant, &desc, i, quadrant->level + 1);
+  if (!p4est_balance_seeds (&desc, &center, balance, NULL)) {
+    if (quadrant->level < refine_level) {
+      return 1;
+    }
+  }
+  ((balance_seeds_elem_t *) (quadrant->p.user_data))->flag = quadrant->level;
+  return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 mpisize, mpirank;
+  p4est_t            *p4est;
+  p4est_connectivity_t *connectivity;
+  sc_dmatrix_t       *vtkvec;
+  p4est_tree_t       *tree;
+  sc_array_t         *quadrants;
+  size_t              zz, count;
+  p4est_quadrant_t   *q;
+  int                 i;
+#ifndef P4_TO_P8
+  const char          filename[] = "p4est_balance_face";
+#else
+  const char          filename[] = "p8est_balance_edge";
+#endif
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+#ifndef P4_TO_P8
+  connectivity = p4est_connectivity_new_unitsquare ();
+#else
+  connectivity = p8est_connectivity_new_unitcube ();
+#endif
+
+  p4est = p4est_new_ext (mpicomm, connectivity, 0, 2, 1,
+                         sizeof (balance_seeds_elem_t), init_fn, NULL);
+
+  p4est_refine (p4est, 1, refine_fn, init_fn);
+
+  p4est_vtk_write_header (p4est, NULL, 1. - 2. * SC_EPS,
+                          0, 0, 0, 0, "level", NULL, filename);
+  vtkvec = sc_dmatrix_new (p4est->local_num_quadrants, P4EST_CHILDREN);
+  tree = p4est_tree_array_index (p4est->trees, 0);
+  quadrants = &(tree->quadrants);
+  count = quadrants->elem_count;
+  for (zz = 0; zz < count; zz++) {
+    q = p4est_quadrant_array_index (quadrants, zz);
+    for (i = 0; i < P4EST_CHILDREN; i++) {
+      vtkvec->e[zz][i] = (double)
+        ((balance_seeds_elem_t *) (q->p.user_data))->flag;
+    }
+  }
+  p4est_vtk_write_point_scalar (p4est, NULL, filename, "level", vtkvec->e[0]);
+  p4est_vtk_write_footer (p4est, filename);
+
+  sc_dmatrix_destroy (vtkvec);
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (connectivity);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/balance_seeds/balance_seeds3.c b/example/balance_seeds/balance_seeds3.c
new file mode 100644
index 0000000..948d1d8
--- /dev/null
+++ b/example/balance_seeds/balance_seeds3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "balance_seeds2.c"
diff --git a/example/mesh/Makefile.am b/example/mesh/Makefile.am
new file mode 100644
index 0000000..949e243
--- /dev/null
+++ b/example/mesh/Makefile.am
@@ -0,0 +1,21 @@
+
+# This file is part of p4est.
+# Makefile.am in example/mesh
+# included non-recursively from toplevel directory
+
+if P4EST_ENABLE_BUILD_2D
+bin_PROGRAMS += example/mesh/p4est_mesh
+example_mesh_p4est_mesh_SOURCES = example/mesh/mesh2.c
+
+LINT_CSOURCES += $(example_mesh_p4est_mesh_SOURCES)
+endif
+
+if P4EST_ENABLE_BUILD_3D
+bin_PROGRAMS += example/mesh/p8est_mesh \
+	example/mesh/p8est_periodicity
+
+example_mesh_p8est_mesh_SOURCES = example/mesh/mesh3.c
+example_mesh_p8est_periodicity_SOURCES = example/mesh/periodicity3.c
+
+LINT_CSOURCES += $(example_mesh_p8est_mesh_SOURCES)
+endif
diff --git a/example/mesh/conndebug.p8c b/example/mesh/conndebug.p8c
new file mode 100644
index 0000000..878c9a3
Binary files /dev/null and b/example/mesh/conndebug.p8c differ
diff --git a/example/mesh/mesh2.c b/example/mesh/mesh2.c
new file mode 100644
index 0000000..f678762
--- /dev/null
+++ b/example/mesh/mesh2.c
@@ -0,0 +1,478 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * Usage: p4est_mesh <configuration> <level>
+ *        possible configurations:
+ *        o unit      Refinement on the unit square.
+ *        o three     Refinement on a forest with three trees.
+ *        o moebius   Refinement on a 5-tree Moebius band.
+ *        o star      Refinement on a 6-tree star shaped domain.
+ *        o periodic  Refinement on the unit square with all-periodic b.c.
+ *        o rotwrap   Refinement on the unit square with weird periodic b.c.
+ */
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_mesh.h>
+#include <p4est_vtk.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_mesh.h>
+#include <p8est_vtk.h>
+#endif
+
+typedef enum
+{
+  P4EST_CONFIG_NULL,
+#ifndef P4_TO_P8
+  P4EST_CONFIG_UNIT,
+  P4EST_CONFIG_THREE,
+  P4EST_CONFIG_MOEBIUS,
+  P4EST_CONFIG_STAR,
+  P4EST_CONFIG_PERIODIC,
+  P4EST_CONFIG_ROTWRAP
+#else
+  P8EST_CONFIG_UNIT,
+  P8EST_CONFIG_PERIODIC,
+  P8EST_CONFIG_ROTWRAP,
+  P8EST_CONFIG_TWOCUBES,
+  P8EST_CONFIG_TWOWRAP,
+  P8EST_CONFIG_ROTCUBES,
+  P8EST_CONFIG_SHELL,
+  P8EST_CONFIG_SPHERE
+#endif
+}
+simple_config_t;
+
+typedef struct
+{
+  p4est_quadrant_t    quad;
+}
+user_data_t;
+
+typedef struct
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpisize;
+  int                 mpirank;
+}
+mpi_context_t;
+
+static int          refine_level = 0;
+
+static void
+init_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+         p4est_quadrant_t * quadrant)
+{
+  user_data_t        *data = (user_data_t *) quadrant->p.user_data;
+
+  data->quad = *quadrant;
+  data->quad.p.which_tree = which_tree;
+}
+
+static int
+refine_uniform (p4est_t * p4est, p4est_topidx_t which_tree,
+                p4est_quadrant_t * quadrant)
+{
+  return (int) quadrant->level < refine_level;
+}
+
+static int
+refine_normal (p4est_t * p4est, p4est_topidx_t which_tree,
+               p4est_quadrant_t * quadrant)
+{
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && p4est_quadrant_child_id (quadrant) == 3) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_LAST_OFFSET (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+#ifndef P4_TO_P8
+  if (quadrant->x >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+#else
+  if (quadrant->z >= P8EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+#endif
+
+  return 1;
+}
+
+static void
+test_mesh (p4est_t * p4est, p4est_ghost_t * ghost, p4est_mesh_t * mesh,
+           int compute_tree_index, int compute_level_lists,
+           p4est_connect_type_t mesh_btype,
+           user_data_t * ghost_data, int uniform)
+{
+  const int           HF = P4EST_HALF * P4EST_FACES;
+  size_t              i;
+  int                 level;
+  int                 f, nf;
+  int                 c;
+  int                 nface;
+  int                 nrank;
+  p4est_topidx_t      which_tree;
+  p4est_locidx_t      K, kl;
+  p4est_locidx_t      ql, QpG, lnC;
+  p4est_locidx_t      qlid, qumid, quadrant_id, which_quad;
+  p4est_mesh_face_neighbor_t mfn, mfn2;
+  p4est_quadrant_t   *q;
+  p4est_tree_t       *tree;
+
+  K = mesh->local_num_quadrants;
+  P4EST_ASSERT (K == p4est->local_num_quadrants);
+  QpG = mesh->local_num_quadrants + mesh->ghost_num_quadrants;
+  lnC = mesh->local_num_corners;
+  P4EST_ASSERT (lnC >= 0);
+
+  P4EST_ASSERT (compute_tree_index == (mesh->quad_to_tree != NULL));
+  P4EST_ASSERT (compute_level_lists == (mesh->quad_level != NULL));
+  P4EST_ASSERT ((mesh_btype == P4EST_CONNECT_CORNER) ==
+                (mesh->quad_to_corner != NULL));
+
+  /* TODO: test the mesh relations in more depth */
+  tree = NULL;
+  for (kl = 0; kl < K; ++kl) {
+    if (compute_tree_index) {
+      tree = p4est_tree_array_index (p4est->trees, mesh->quad_to_tree[kl]);
+      SC_CHECK_ABORTF (tree->quadrants_offset <= kl && kl <
+                       tree->quadrants_offset +
+                       (p4est_locidx_t) tree->quadrants.elem_count,
+                       "Tree index mismatch %lld", (long long) kl);
+    }
+
+    if (mesh_btype == P4EST_CONNECT_CORNER) {
+      for (c = 0; c < P4EST_CHILDREN; ++c) {
+        qlid = mesh->quad_to_corner[P4EST_CHILDREN * kl + c];
+        SC_CHECK_ABORTF (qlid >= -2
+                         && qlid < QpG + lnC, "quad %lld corner %d mismatch",
+                         (long long) kl, c);
+      }
+    }
+    for (f = 0; f < P4EST_FACES; ++f) {
+      ql = mesh->quad_to_quad[P4EST_FACES * kl + f];
+      SC_CHECK_ABORTF (0 <= ql && ql < QpG,
+                       "quad %d face %d neighbor %d mismatch", kl, f, ql);
+      nf = mesh->quad_to_face[P4EST_FACES * kl + f];
+      if (uniform) {
+        SC_CHECK_ABORTF (0 <= nf && nf < HF,
+                         "quad %d face %d code %d mismatch", kl, f, nf);
+      }
+      else {
+        SC_CHECK_ABORTF (-HF <= nf && nf < (P4EST_HALF + 1) * HF,
+                         "quad %d face %d code %d mismatch", kl, f, nf);
+      }
+    }
+  }
+
+  /* Test the level lists */
+  if (compute_tree_index && compute_level_lists) {
+    for (level = 0; level < P4EST_QMAXLEVEL; ++level) {
+      for (i = 0; i < mesh->quad_level[level].elem_count; ++i) {
+        /* get the local quadrant id */
+        quadrant_id =
+          *(p4est_locidx_t *) sc_array_index (&mesh->quad_level[level], i);
+
+        /* get the tree it belongs to */
+        kl = mesh->quad_to_tree[quadrant_id];
+        tree = p4est_tree_array_index (p4est->trees, kl);
+
+        /* and finally, get the actual quadrant from the tree quadrant list */
+        quadrant_id -= tree->quadrants_offset;
+        q =
+          p4est_quadrant_array_index (&tree->quadrants, (size_t) quadrant_id);
+
+        SC_CHECK_ABORTF (q->level == level,
+                         "quad %d level %d mismatch", quadrant_id, level);
+      }
+    }
+  }
+
+  /* Test face neighbor iterator */
+  for (qumid = 0; qumid < mesh->local_num_quadrants; ++qumid) {
+    which_tree = -1;
+    q = p4est_mesh_quadrant_cumulative (p4est, qumid,
+                                        &which_tree, &quadrant_id);
+    p4est_mesh_face_neighbor_init2 (&mfn, p4est, ghost, mesh,
+                                    which_tree, quadrant_id);
+    p4est_mesh_face_neighbor_init (&mfn2, p4est, ghost, mesh, which_tree, q);
+    P4EST_ASSERT (mfn2.quadrant_id == quadrant_id);
+    while ((q = p4est_mesh_face_neighbor_next (&mfn, &which_tree, &which_quad,
+                                               &nface, &nrank)) != NULL) {
+#ifdef P4EST_ENABLE_DEBUG
+      user_data_t        *data;
+
+      data = (user_data_t *) p4est_mesh_face_neighbor_data (&mfn, ghost_data);
+
+      P4EST_ASSERT (p4est_quadrant_is_equal (q, &(data->quad)));
+      P4EST_ASSERT (data->quad.p.which_tree == which_tree);
+#endif
+    }
+  }
+}
+
+static void
+mesh_run (mpi_context_t * mpi, p4est_connectivity_t * connectivity,
+          int uniform, int compute_tree_index, int compute_level_lists,
+          p4est_connect_type_t mesh_btype)
+{
+  int                 mpiret;
+  unsigned            crc;
+  long                local_used[4], global_used[4];
+  p4est_t            *p4est;
+  p4est_ghost_t      *ghost;
+  p4est_mesh_t       *mesh;
+  user_data_t        *ghost_data;
+
+  p4est = p4est_new (mpi->mpicomm, connectivity,
+                     sizeof (user_data_t), init_fn, NULL);
+  if (!uniform)
+    p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_mesh_new");
+
+  /* refinement */
+  if (uniform) {
+    p4est_refine (p4est, 1, refine_uniform, init_fn);
+  }
+  else {
+    p4est_refine (p4est, 1, refine_normal, init_fn);
+    p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_mesh_refined");
+  }
+
+  /* balance */
+  p4est_balance (p4est, P4EST_CONNECT_FULL, init_fn);
+  if (!uniform)
+    p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_mesh_balanced");
+
+  /* partition */
+  p4est_partition (p4est, 0, NULL);
+  if (!uniform) {
+    p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_mesh_partition");
+  }
+  crc = p4est_checksum (p4est);
+
+  /* print and verify forest checksum */
+  P4EST_GLOBAL_STATISTICSF ("Tree %s checksum 0x%08x\n",
+                            uniform ? "uniform" : "adapted", crc);
+
+  /* create ghost layer and mesh */
+  ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+  ghost_data = P4EST_ALLOC (user_data_t, ghost->ghosts.elem_count);
+  p4est_ghost_exchange_data (p4est, ghost, ghost_data);
+  mesh = p4est_mesh_new_ext (p4est, ghost,
+                             compute_tree_index, compute_level_lists,
+                             mesh_btype);
+  test_mesh (p4est, ghost, mesh,
+             compute_tree_index, compute_level_lists, mesh_btype,
+             ghost_data, uniform);
+
+  /* compute memory used */
+  local_used[0] = (long) p4est_connectivity_memory_used (p4est->connectivity);
+  local_used[1] = (long) p4est_memory_used (p4est);
+  local_used[2] = (long) p4est_ghost_memory_used (ghost);
+  local_used[3] = (long) p4est_mesh_memory_used (mesh);
+  mpiret = sc_MPI_Allreduce (local_used, global_used, 4, sc_MPI_LONG,
+                             sc_MPI_SUM, mpi->mpicomm);
+  SC_CHECK_MPI (mpiret);
+  P4EST_GLOBAL_PRODUCTIONF ("Total %s memory used %ld %ld %ld %ld\n",
+                            uniform ? "uniform" : "adapted",
+                            global_used[0], global_used[1],
+                            global_used[2], global_used[3]);
+
+  /* destroy ghost layer and mesh */
+  P4EST_FREE (ghost_data);
+  p4est_mesh_destroy (mesh);
+  p4est_ghost_destroy (ghost);
+
+  /* destroy the p4est structure */
+  p4est_destroy (p4est);
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 wrongusage;
+  const char         *usage;
+  mpi_context_t       mpi_context, *mpi = &mpi_context;
+  p4est_connectivity_t *connectivity;
+  simple_config_t     config;
+
+  /* initialize MPI and p4est internals */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpi->mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpi->mpicomm, &mpi->mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpi->mpicomm, &mpi->mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpi->mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* process command line arguments */
+  usage =
+    "Arguments: <configuration> <level>\n   Configuration can be any of\n"
+#ifndef P4_TO_P8
+    "      unit|three|moebius|star|periodic|rotwrap\n"
+#else
+    "      unit|periodic|rotwrap|twocubes|twowrap|rotcubes|shell|sphere\n"
+#endif
+    "   Level controls the maximum depth of refinement\n";
+  wrongusage = 0;
+  config = P4EST_CONFIG_NULL;
+  if (!wrongusage && argc != 3) {
+    wrongusage = 1;
+  }
+  if (!wrongusage) {
+    if (!strcmp (argv[1], "unit")) {
+#ifndef P4_TO_P8
+      config = P4EST_CONFIG_UNIT;
+#else
+      config = P8EST_CONFIG_UNIT;
+#endif
+    }
+#ifndef P4_TO_P8
+    else if (!strcmp (argv[1], "three")) {
+      config = P4EST_CONFIG_THREE;
+    }
+    else if (!strcmp (argv[1], "moebius")) {
+      config = P4EST_CONFIG_MOEBIUS;
+    }
+    else if (!strcmp (argv[1], "star")) {
+      config = P4EST_CONFIG_STAR;
+    }
+    else if (!strcmp (argv[1], "periodic")) {
+      config = P4EST_CONFIG_PERIODIC;
+    }
+    else if (!strcmp (argv[1], "rotwrap")) {
+      config = P4EST_CONFIG_ROTWRAP;
+    }
+#else
+    else if (!strcmp (argv[1], "periodic")) {
+      config = P8EST_CONFIG_PERIODIC;
+    }
+    else if (!strcmp (argv[1], "rotwrap")) {
+      config = P8EST_CONFIG_ROTWRAP;
+    }
+    else if (!strcmp (argv[1], "twocubes")) {
+      config = P8EST_CONFIG_TWOCUBES;
+    }
+    else if (!strcmp (argv[1], "twowrap")) {
+      config = P8EST_CONFIG_TWOWRAP;
+    }
+    else if (!strcmp (argv[1], "rotcubes")) {
+      config = P8EST_CONFIG_ROTCUBES;
+    }
+    else if (!strcmp (argv[1], "shell")) {
+      config = P8EST_CONFIG_SHELL;
+    }
+    else if (!strcmp (argv[1], "sphere")) {
+      config = P8EST_CONFIG_SPHERE;
+    }
+#endif
+    else {
+      wrongusage = 1;
+    }
+  }
+  if (wrongusage) {
+    P4EST_GLOBAL_LERROR (usage);
+    sc_abort_collective ("Usage error");
+  }
+
+  /* assign variables based on configuration */
+  refine_level = atoi (argv[2]);
+
+  /* create connectivity and forest structures */
+  if (0) {
+  }
+#ifndef P4_TO_P8
+  else if (config == P4EST_CONFIG_THREE) {
+    connectivity = p4est_connectivity_new_corner ();
+  }
+  else if (config == P4EST_CONFIG_MOEBIUS) {
+    connectivity = p4est_connectivity_new_moebius ();
+  }
+  else if (config == P4EST_CONFIG_STAR) {
+    connectivity = p4est_connectivity_new_star ();
+  }
+  else if (config == P4EST_CONFIG_PERIODIC) {
+    connectivity = p4est_connectivity_new_periodic ();
+  }
+  else if (config == P4EST_CONFIG_ROTWRAP) {
+    connectivity = p4est_connectivity_new_rotwrap ();
+  }
+#else
+  else if (config == P8EST_CONFIG_PERIODIC) {
+    connectivity = p8est_connectivity_new_periodic ();
+  }
+  else if (config == P8EST_CONFIG_ROTWRAP) {
+    connectivity = p8est_connectivity_new_rotwrap ();
+  }
+  else if (config == P8EST_CONFIG_TWOCUBES) {
+    connectivity = p8est_connectivity_new_twocubes ();
+  }
+  else if (config == P8EST_CONFIG_TWOWRAP) {
+    connectivity = p8est_connectivity_new_twowrap ();
+  }
+  else if (config == P8EST_CONFIG_ROTCUBES) {
+    connectivity = p8est_connectivity_new_rotcubes ();
+  }
+  else if (config == P8EST_CONFIG_SHELL) {
+    connectivity = p8est_connectivity_new_shell ();
+  }
+  else if (config == P8EST_CONFIG_SPHERE) {
+    connectivity = p8est_connectivity_new_sphere ();
+  }
+#endif
+  else {
+#ifndef P4_TO_P8
+    connectivity = p4est_connectivity_new_unitsquare ();
+#else
+    connectivity = p8est_connectivity_new_unitcube ();
+#endif
+  }
+
+  /* run mesh tests */
+  mesh_run (mpi, connectivity, 1, 0, 1, P4EST_CONNECT_FULL);
+  mesh_run (mpi, connectivity, 0, 1, 0, P4EST_CONNECT_FULL);
+  mesh_run (mpi, connectivity, 0, 0, 0, P4EST_CONNECT_FACE);
+  mesh_run (mpi, connectivity, 1, 1, 1, P4EST_CONNECT_FACE);
+
+  /* clean up and exit */
+  p4est_connectivity_destroy (connectivity);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/mesh/mesh3.c b/example/mesh/mesh3.c
new file mode 100644
index 0000000..fdefa5b
--- /dev/null
+++ b/example/mesh/mesh3.c
@@ -0,0 +1,38 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * Usage: p8est_mesh <configuration> <level>
+ *        possible configurations:
+ *        o unit      Refinement on the unit cube.
+ *        o periodic  Refinement on the all-periodic unit cube.
+ *        o rotwrap   Refinement on a funny some-periodic unit cube.
+ *        o twocubes  Refinement on two connected cubes.
+ *        o twowrap   Refinement on two connected and wrapped cubes.
+ *        o rotcubes  Refinement on a 6-tree configuration.
+ *        o shell     Refinement on a 24-tree spherical shell.
+ *        o sphere    Refinement on a 13-tree solid sphere.
+ */
+
+#include <p4est_to_p8est.h>
+#include "mesh2.c"
diff --git a/example/mesh/periodicity3.c b/example/mesh/periodicity3.c
new file mode 100644
index 0000000..58e663b
--- /dev/null
+++ b/example/mesh/periodicity3.c
@@ -0,0 +1,72 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#ifdef P4_TO_P8
+#include <p8est_connectivity.h>
+
+int
+main (int argc, char **argv)
+{
+  int                 i, j;
+  int                 t0, t1;
+  size_t              bytes;
+  const char         *filename = "conndebug.p8c";
+  p4est_connectivity_t *conn;
+
+  conn = p4est_connectivity_load (filename, &bytes);
+  if (conn == NULL) {
+    P4EST_LERRORF ("Could not read file %s\n", filename);
+    return 1;
+  }
+  P4EST_INFOF ("Read %d bytes\n", (int) bytes);
+
+  p4est_connectivity_complete (conn);
+
+  for (i = 0; i < 4; ++i) {
+    t0 = i + 0;
+    t1 = i + 4;
+    P4EST_VERBOSEF ("X face %d: %d %d\n", i, t0, t1);
+    p4est_connectivity_join_faces (conn, t0, t1, 0, 1, 0);
+  }
+  for (i = 0; i < 2; ++i) {
+    for (j = 0; j < 2; ++j) {
+      t0 = 4 * i + j + 0;
+      t1 = 4 * i + j + 2;
+      P4EST_VERBOSEF ("Y face %d: %d %d\n", 2 * i + j, t0, t1);
+      p4est_connectivity_join_faces (conn, t0, t1, 2, 3, 0);
+    }
+  }
+  for (i = 0; i < 4; ++i) {
+    t0 = 2 * i + 0;
+    t1 = 2 * i + 1;
+    P4EST_VERBOSEF ("Z face %d: %d %d\n", i, t0, t1);
+    p4est_connectivity_join_faces (conn, t0, t1, 4, 5, 0);
+  }
+
+  p4est_connectivity_destroy (conn);
+
+  return 0;
+}
+
+#endif /* P4_TO_P8 */
diff --git a/example/p6est/Makefile.am b/example/p6est/Makefile.am
new file mode 100644
index 0000000..65cd860
--- /dev/null
+++ b/example/p6est/Makefile.am
@@ -0,0 +1,25 @@
+
+if P4EST_ENABLE_BUILD_P6EST
+libp4est_installed_headers += \
+        example/p6est/p6est.h example/p6est/p6est_ghost.h example/p6est/p6est_lnodes.h \
+        example/p6est/p6est_profile.h example/p6est/p6est_vtk.h example/p6est/p6est_io.h \
+        example/p6est/p6est_extended.h
+libp4est_compiled_sources += \
+        example/p6est/p6est.c example/p6est/p6est_ghost.c example/p6est/p6est_lnodes.c \
+        example/p6est/p6est_profile.c example/p6est/p6est_vtk.c example/p6est/p6est_io.c
+AM_CPPFLAGS += -I at top_srcdir@/example/p6est
+
+p6est_test_programs = example/p6est/test/p6est_test_all
+
+example_p6est_test_p6est_test_all_SOURCES = example/p6est/test/test_all.c
+TESTS += $(p6est_test_programs)
+check_PROGRAMS += $(p6est_test_programs)
+
+# for now -- make it easier for batch builds
+bin_PROGRAMS += $(p6est_test_programs)
+endif
+
+clean-local-p6est:
+	rm -f *.p6p
+
+.PHONY: clean-local-p6est
diff --git a/example/p6est/p6est.c b/example/p6est/p6est.c
new file mode 100644
index 0000000..b16320b
--- /dev/null
+++ b/example/p6est/p6est.c
@@ -0,0 +1,2337 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2013 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p6est.h>
+#include <p6est_ghost.h>
+#include <p6est_profile.h>
+#include <p4est_lnodes.h>
+#include <p8est.h>
+#include <p4est_extended.h>
+#include <sc_containers.h>
+#include <p4est_communication.h>
+#include <sc_io.h>
+#include <p6est_extended.h>
+
+/* htonl is in either of these two */
+#ifdef P4EST_HAVE_ARPA_NET_H
+#include <arpa/inet.h>
+#endif
+#ifdef P4EST_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef P4EST_HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+p6est_connectivity_t *
+p6est_connectivity_new (p4est_connectivity_t * conn4,
+                        double *top_vertices, double height[3])
+{
+  p6est_connectivity_t *conn = P4EST_ALLOC (p6est_connectivity_t, 1);
+
+  conn->conn4 = p4est_connectivity_new_copy (conn4->num_vertices,
+                                             conn4->num_trees,
+                                             conn4->num_corners,
+                                             conn4->vertices,
+                                             conn4->tree_to_vertex,
+                                             conn4->tree_to_tree,
+                                             conn4->tree_to_face,
+                                             conn4->tree_to_corner,
+                                             conn4->ctt_offset,
+                                             conn4->corner_to_tree,
+                                             conn4->corner_to_corner);
+
+  if (top_vertices != NULL) {
+    conn->top_vertices = P4EST_ALLOC (double, 3 * conn4->num_vertices);
+    memcpy (conn->top_vertices, top_vertices,
+            3 * conn4->num_vertices * sizeof (double));
+  }
+  else {
+    conn->top_vertices = NULL;
+    P4EST_ASSERT (height != NULL);
+    conn->height[0] = height[0];
+    conn->height[1] = height[1];
+    conn->height[2] = height[2];
+  }
+
+  return conn;
+}
+
+void
+p6est_connectivity_destroy (p6est_connectivity_t * conn)
+{
+  p4est_connectivity_destroy (conn->conn4);
+  if (conn->top_vertices != NULL) {
+    P4EST_FREE (conn->top_vertices);
+  }
+  P4EST_FREE (conn);
+}
+
+void
+p6est_tree_get_vertices (p6est_connectivity_t * conn,
+                         p4est_topidx_t which_tree, double vertices[24])
+{
+  p4est_connectivity_t *conn4 = conn->conn4;
+  const double       *btv = conn4->vertices;
+  const double       *ttv = conn->top_vertices;
+  int                 i, j, k;
+  double             *height = conn->height;
+  double              zerooff[3] = { 0., 0., 0. };
+
+  P4EST_ASSERT (conn4->num_vertices > 0);
+  P4EST_ASSERT (btv != NULL);
+  P4EST_ASSERT (which_tree >= 0 && which_tree < conn4->num_trees);
+  P4EST_ASSERT (vertices != NULL);
+
+  for (k = 0; k < 2; k++) {
+    const double       *verts = k ? ttv : btv;
+    const double       *off = (verts != NULL) ? zerooff : height;
+
+    if (verts == NULL) {
+      verts = btv;
+    }
+
+    for (i = 0; i < P4EST_CHILDREN; i++) {
+      p4est_topidx_t      vert;
+
+      vert = conn4->tree_to_vertex[P4EST_CHILDREN * which_tree + i];
+      for (j = 0; j < 3; j++) {
+        vertices[3 * (P4EST_CHILDREN * k + i) + j] = verts[3 * vert + j] +
+          off[j];
+      }
+    }
+  }
+}
+
+void
+p6est_qcoord_to_vertex (p6est_connectivity_t * conn,
+                        p4est_topidx_t treeid,
+                        p4est_qcoord_t x, p4est_qcoord_t y,
+                        p4est_qcoord_t z, double vxyz[3])
+{
+  double              bottom[3], top[3];
+  double              eta = (double) z / (double) P4EST_ROOT_LEN;
+
+  p4est_qcoord_to_vertex (conn->conn4, treeid, x, y, bottom);
+  if (conn->top_vertices != NULL) {
+    double             *orig = conn->conn4->vertices;
+
+    conn->conn4->vertices = conn->top_vertices;
+    p4est_qcoord_to_vertex (conn->conn4, treeid, x, y, top);
+    conn->conn4->vertices = orig;
+  }
+  else {
+    top[0] = bottom[0] + conn->height[0];
+    top[1] = bottom[1] + conn->height[1];
+    top[2] = bottom[2] + conn->height[2];
+  }
+  vxyz[0] = (1. - eta) * bottom[0] + eta * top[0];
+  vxyz[1] = (1. - eta) * bottom[1] + eta * top[1];
+  vxyz[2] = (1. - eta) * bottom[2] + eta * top[2];
+}
+
+size_t
+p6est_connectivity_memory_used (p6est_connectivity_t * conn)
+{
+  return
+    p4est_connectivity_memory_used (conn->conn4) +
+    conn->top_vertices == NULL ? 0 :
+    (conn->conn4->num_vertices * 3 * sizeof (double));
+}
+
+int
+p6est_connectivity_extra_sink (p6est_connectivity_t * conn,
+                               sc_io_sink_t * sink)
+{
+  int                 retval;
+  size_t              u64z, tcount;
+  double             *v;
+  uint64_t            num_vertices;
+
+  u64z = sizeof (uint64_t);
+  num_vertices = (conn->top_vertices != NULL) ?
+    (uint64_t) conn->conn4->num_vertices : 0;
+
+  retval = sc_io_sink_write (sink, &num_vertices, 1 * u64z);
+
+  if (conn->top_vertices != NULL) {
+    tcount = (size_t) (3 * conn->conn4->num_vertices);
+    v = conn->top_vertices;
+  }
+  else {
+    tcount = 3;
+    v = conn->height;
+  }
+  retval = retval || sc_io_sink_write (sink, v, tcount * sizeof (double));
+
+  return retval;
+}
+
+p6est_connectivity_t *
+p6est_connectivity_extra_source (p4est_connectivity_t * conn4,
+                                 sc_io_source_t * source)
+{
+  p6est_connectivity_t *conn;
+  int                 retval;
+  size_t              u64z;
+  uint64_t            num_vertices;
+  double             *top_vertices;
+  double              height[3];
+
+  u64z = sizeof (uint64_t);
+
+  retval = sc_io_source_read (source, &num_vertices, 1 * u64z, NULL);
+  if (retval) {
+    return NULL;
+  }
+  if (num_vertices > 0) {
+    height[0] = height[1] = height[2] = 0.;
+
+    if (num_vertices != (uint64_t) conn4->num_vertices) {
+      return NULL;
+    }
+    top_vertices = P4EST_ALLOC (double, 3 * num_vertices);
+    retval = sc_io_source_read (source, top_vertices,
+                                3 * num_vertices * sizeof (double), NULL);
+
+    if (retval) {
+      P4EST_FREE (top_vertices);
+      return NULL;
+    }
+  }
+  else {
+    top_vertices = NULL;
+    retval = sc_io_source_read (source, height, 3 * sizeof (double), NULL);
+    if (retval) {
+      return NULL;
+    }
+  }
+  conn = P4EST_ALLOC (p6est_connectivity_t, 1);
+
+  conn->conn4 = conn4;
+  conn->top_vertices = top_vertices;
+  conn->height[0] = height[0];
+  conn->height[1] = height[1];
+  conn->height[2] = height[2];
+
+  return conn;
+}
+
+int
+p6est_connectivity_sink (p6est_connectivity_t * conn, sc_io_sink_t * sink)
+{
+  int                 retval;
+
+  retval = p4est_connectivity_sink (conn->conn4, sink);
+
+  retval = retval || p6est_connectivity_extra_sink (conn, sink);
+
+  return retval;
+}
+
+p6est_connectivity_t *
+p6est_connectivity_source (sc_io_source_t * source)
+{
+  p4est_connectivity_t *conn4;
+
+  conn4 = p4est_connectivity_source (source);
+  if (conn4 == NULL) {
+    return NULL;
+  }
+
+  return p6est_connectivity_extra_source (conn4, source);
+}
+
+int
+p6est_connectivity_save (const char *filename, p6est_connectivity_t * conn)
+{
+  int                 retval;
+  sc_io_sink_t       *sink;
+
+  sink = sc_io_sink_new (SC_IO_TYPE_FILENAME, SC_IO_MODE_WRITE,
+                         SC_IO_ENCODE_NONE, filename);
+  if (sink == NULL) {
+    return -1;
+  }
+
+  /* Close file even on earlier write error */
+  retval = p6est_connectivity_sink (conn, sink);
+  retval = sc_io_sink_destroy (sink) || retval;
+
+  return retval;
+}
+
+p6est_connectivity_t *
+p6est_connectivity_load (const char *filename, size_t * bytes)
+{
+  int                 retval;
+  size_t              bytes_in;
+  sc_io_source_t     *source;
+  p6est_connectivity_t *conn;
+
+  source = sc_io_source_new (SC_IO_TYPE_FILENAME,
+                             SC_IO_ENCODE_NONE, filename);
+  if (source == NULL) {
+    return NULL;
+  }
+
+  /* Get byte length and close file even on earlier read error */
+  conn = p6est_connectivity_source (source);
+  retval = sc_io_source_complete (source, &bytes_in, NULL) || conn == NULL;
+  retval = sc_io_source_destroy (source) || retval;
+  if (retval) {
+    if (conn != NULL) {
+      p6est_connectivity_destroy (conn);
+    }
+    return NULL;
+  }
+
+  if (bytes != NULL) {
+    *bytes = bytes_in;
+  }
+  return conn;
+}
+
+size_t
+p6est_memory_used (p6est_t * p6est)
+{
+  size_t              size;
+
+  size = p4est_memory_used (p6est->columns);
+  size += sc_array_memory_used (p6est->layers, 1);
+  if (p6est->data_size > 0) {
+    size += sc_mempool_memory_used (p6est->user_data_pool);
+  }
+  size += sc_mempool_memory_used (p6est->layer_pool);
+
+  return size;
+}
+
+typedef struct p6est_init_data
+{
+  int                 min_zlevel;
+  sc_array_t         *layers;
+  p6est_init_t        init_fn;
+  void               *user_pointer;
+}
+p6est_init_data_t;
+
+static void
+p6est_init_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+               p4est_quadrant_t * col)
+{
+  p6est_t            *p6est = (p6est_t *) p4est->user_pointer;
+  p6est_init_data_t  *init_data = (p6est_init_data_t *) p6est->user_pointer;
+  int                 nlayers = 1 << (init_data->min_zlevel);
+  sc_array_t         *layers = init_data->layers;
+  size_t              incount = layers->elem_count, zz;
+  size_t              last = incount + nlayers;
+  p2est_quadrant_t   *layer;
+
+  /* we have to sneak the user_pointer in */
+  p6est->user_pointer = init_data->user_pointer;
+
+  P6EST_COLUMN_SET_RANGE (col, layers->elem_count, last);
+
+  layer = (p2est_quadrant_t *) sc_array_push_count (layers, nlayers);
+
+  for (zz = incount; zz < last; zz++, layer++) {
+    P2EST_QUADRANT_INIT (layer);
+
+    layer->level = init_data->min_zlevel;
+    layer->z = (zz - incount) * P4EST_QUADRANT_LEN (layer->level);
+
+    p6est_layer_init_data (p6est, which_tree, col, layer, init_data->init_fn);
+  }
+
+  /* we have to sneak the user_pointer out */
+  p6est->user_pointer = (void *) init_data;
+}
+
+p6est_t            *
+p6est_new_ext (sc_MPI_Comm mpicomm, p6est_connectivity_t * connectivity,
+               p4est_locidx_t min_quadrants, int min_level, int min_zlevel,
+               int fill_uniform, size_t data_size, p6est_init_t init_fn,
+               void *user_pointer)
+{
+  p6est_t            *p6est = P4EST_ALLOC (p6est_t, 1);
+  p4est_t            *p4est;
+  sc_array_t         *layers;
+  sc_mempool_t       *user_data_pool;
+  p6est_init_data_t   init_data;
+  int                 mpiret, num_procs, rank;
+  int                 quadpercol = (1 << min_zlevel);
+  int                 i;
+
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Into p6est_new with min layers %lld z-level %d\n",
+     (long long) min_quadrants, SC_MAX (min_zlevel, 0));
+  p4est_log_indent_push ();
+
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  layers = sc_array_new (sizeof (p2est_quadrant_t));
+
+  if (data_size > 0) {
+    user_data_pool = sc_mempool_new (data_size);
+  }
+  else {
+    user_data_pool = NULL;
+  }
+
+  p6est->layer_pool = sc_mempool_new (sizeof (p2est_quadrant_t));
+
+  p6est->mpicomm = mpicomm;
+  p6est->mpisize = num_procs;
+  p6est->mpirank = rank;
+  p6est->data_size = data_size;
+  p6est->user_pointer = user_pointer;
+  p6est->connectivity = connectivity;
+  p6est->layers = layers;
+  p6est->user_data_pool = user_data_pool;
+
+  P4EST_ASSERT (min_zlevel <= P4EST_QMAXLEVEL);
+
+  init_data.min_zlevel = min_zlevel;
+  init_data.layers = layers;
+  init_data.init_fn = init_fn;
+  init_data.user_pointer = user_pointer;
+  p6est->user_pointer = &init_data;
+
+  P4EST_GLOBAL_VERBOSE ("Creating p4est for columns\n");
+  p4est =
+    p4est_new_ext (mpicomm, connectivity->conn4, min_quadrants / quadpercol,
+                   min_level, fill_uniform, 0, p6est_init_fn, (void *) p6est);
+
+  p6est->user_pointer = user_pointer;
+  p6est->columns = p4est;
+  p6est->global_first_layer = P4EST_ALLOC (p4est_gloidx_t, num_procs + 1);
+  for (i = 0; i <= num_procs; i++) {
+    p6est->global_first_layer[i] =
+      quadpercol * p4est->global_first_quadrant[i];
+  }
+
+  /* print more statistics */
+  P4EST_VERBOSEF ("total local layers %lld\n",
+                  (long long) layers->elem_count);
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Done p6est_new with %lld total layers in %lld total columns\n",
+     (long long) p6est->global_first_layer[p6est->mpisize],
+     (long long) p6est->columns->global_num_quadrants);
+
+  return p6est;
+}
+
+p6est_t            *
+p6est_new (sc_MPI_Comm mpicomm, p6est_connectivity_t * connectivity,
+           size_t data_size, p6est_init_t init_fn, void *user_pointer)
+{
+  return p6est_new_ext (mpicomm, connectivity, 0, 0, 0, 1,
+                        data_size, init_fn, user_pointer);
+}
+
+p6est_t            *
+p6est_new_from_p4est (p4est_t * p4est, double *top_vertices, double height[3],
+                      int min_zlevel, size_t data_size, p6est_init_t init_fn,
+                      void *user_pointer)
+{
+  p6est_t            *p6est = P4EST_ALLOC (p6est_t, 1);
+  p6est_connectivity_t *conn;
+  sc_array_t         *layers;
+  sc_mempool_t       *user_data_pool;
+  p6est_init_data_t   init_data;
+  int                 num_procs;
+  int                 quadpercol = (1 << min_zlevel);
+  int                 i;
+
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Into p6est_new_from_p4est with z-level %d\n", SC_MAX (min_zlevel, 0));
+  p4est_log_indent_push ();
+
+  layers = sc_array_new (sizeof (p2est_quadrant_t));
+
+  if (data_size > 0) {
+    user_data_pool = sc_mempool_new (data_size);
+  }
+  else {
+    user_data_pool = NULL;
+  }
+
+  conn = p6est_connectivity_new (p4est->connectivity, top_vertices, height);
+
+  p6est->layer_pool = sc_mempool_new (sizeof (p2est_quadrant_t));
+
+  p6est->mpicomm = p4est->mpicomm;
+  p6est->mpisize = num_procs = p4est->mpisize;
+  p6est->mpirank = p4est->mpirank;
+  p6est->data_size = data_size;
+  p6est->user_pointer = user_pointer;
+  p6est->connectivity = conn;
+  p6est->layers = layers;
+  p6est->user_data_pool = user_data_pool;
+  p6est->columns = p4est_copy (p4est, 0);
+  p6est->columns->connectivity = conn->conn4;
+
+  P4EST_ASSERT (min_zlevel <= P4EST_QMAXLEVEL);
+
+  init_data.min_zlevel = min_zlevel;
+  init_data.layers = layers;
+  init_data.init_fn = init_fn;
+  init_data.user_pointer = user_pointer;
+  p6est->user_pointer = &init_data;
+
+  p4est_reset_data (p6est->columns, 0, p6est_init_fn, (void *) p6est);
+
+  p6est->user_pointer = user_pointer;
+  p6est->global_first_layer = P4EST_ALLOC (p4est_gloidx_t, num_procs + 1);
+  for (i = 0; i <= num_procs; i++) {
+    p6est->global_first_layer[i] =
+      quadpercol * p4est->global_first_quadrant[i];
+  }
+
+  /* print more statistics */
+  P4EST_VERBOSEF ("total local layers %lld\n",
+                  (long long) layers->elem_count);
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Done p6est_new with %lld total layers in %lld total columns\n",
+     (long long) p6est->global_first_layer[p6est->mpisize],
+     (long long) p6est->columns->global_num_quadrants);
+
+  return p6est;
+}
+
+void
+p6est_destroy (p6est_t * p6est)
+{
+  sc_array_t         *layers = p6est->layers;
+  size_t              layercount = layers->elem_count;
+  size_t              zz;
+
+  for (zz = 0; zz < layercount; zz++) {
+    p2est_quadrant_t   *layer = p2est_quadrant_array_index (layers, zz);
+
+    p6est_layer_free_data (p6est, layer);
+  }
+  sc_array_destroy (p6est->layers);
+
+  p4est_destroy (p6est->columns);
+  if (p6est->user_data_pool != NULL) {
+    sc_mempool_destroy (p6est->user_data_pool);
+  }
+  sc_mempool_destroy (p6est->layer_pool);
+  P4EST_FREE (p6est->global_first_layer);
+  P4EST_FREE (p6est);
+}
+
+p6est_t            *
+p6est_copy (p6est_t * input, int copy_data)
+{
+  p6est_t            *p6est = P4EST_ALLOC (p6est_t, 1);
+  size_t              zz, qcount = input->layers->elem_count;
+
+  memcpy (p6est, input, sizeof (p6est_t));
+  p6est->layers =
+    sc_array_new_size (input->layers->elem_size, input->layers->elem_count);
+  sc_array_copy (p6est->layers, input->layers);
+  p6est->columns = p4est_copy (input->columns, 0);
+  p6est->columns->user_pointer = p6est;
+  if (copy_data && p6est->data_size > 0) {
+    p6est->user_data_pool = sc_mempool_new (p6est->data_size);
+  }
+  else {
+    p6est->data_size = 0;
+  }
+  p6est->layer_pool = sc_mempool_new (sizeof (p2est_quadrant_t));
+
+  if (p6est->data_size > 0) {
+    P4EST_ASSERT (copy_data);
+    for (zz = 0; zz < qcount; zz++) {
+      p2est_quadrant_t   *inlayer =
+        p2est_quadrant_array_index (input->layers, zz);
+      p2est_quadrant_t   *outlayer =
+        p2est_quadrant_array_index (p6est->layers, zz);
+
+      outlayer->p.user_data = sc_mempool_alloc (p6est->user_data_pool);
+      memcpy (outlayer->p.user_data, inlayer->p.user_data, p6est->data_size);
+    }
+  }
+  p6est->global_first_layer =
+    P4EST_ALLOC (p4est_gloidx_t, p6est->mpisize + 1);
+  memcpy (p6est->global_first_layer, input->global_first_layer,
+          (p6est->mpisize + 1) * sizeof (p4est_gloidx_t));
+  return p6est;
+}
+
+void
+p6est_reset_data (p6est_t * p6est, size_t data_size, p6est_init_t init_fn,
+                  void *user_pointer)
+{
+  int                 doresize;
+  size_t              zz, zy, first, last;
+  p4est_topidx_t      jt;
+  p4est_quadrant_t   *col;
+  p2est_quadrant_t   *q;
+  p4est_tree_t       *tree;
+  sc_array_t         *tquadrants;
+
+  doresize = (p6est->data_size != data_size);
+
+  p6est->data_size = data_size;
+  p6est->user_pointer = user_pointer;
+
+  if (doresize) {
+    if (p6est->user_data_pool != NULL) {
+      sc_mempool_destroy (p6est->user_data_pool);
+    }
+    if (p6est->data_size > 0) {
+      p6est->user_data_pool = sc_mempool_new (p6est->data_size);
+    }
+    else {
+      p6est->user_data_pool = NULL;
+    }
+  }
+
+  for (jt = p6est->columns->first_local_tree;
+       jt <= p6est->columns->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (p6est->columns->trees, jt);
+    tquadrants = &tree->quadrants;
+    for (zz = 0; zz < tquadrants->elem_count; ++zz) {
+      col = p4est_quadrant_array_index (tquadrants, zz);
+      P6EST_COLUMN_GET_RANGE (col, &first, &last);
+      for (zy = first; zy < last; zy++) {
+        q = p2est_quadrant_array_index (p6est->layers, zy);
+        if (doresize) {
+          if (p6est->data_size > 0) {
+            q->p.user_data = sc_mempool_alloc (p6est->user_data_pool);
+          }
+          else {
+            q->p.user_data = NULL;
+          }
+        }
+        if (init_fn != NULL) {
+          init_fn (p6est, jt, col, q);
+        }
+      }
+    }
+  }
+}
+
+void
+p6est_save (const char *filename, p6est_t * p6est, int save_data)
+{
+  p6est_save_ext (filename, p6est, save_data, 1);
+}
+
+void
+p6est_save_ext (const char *filename, p6est_t * p6est,
+                int save_data, int save_partition)
+{
+  FILE               *file;
+  int                 rank = p6est->mpirank;
+  long                fpos = -1, foffset;
+  int                 align = 32;
+  uint64_t            u64a;
+  sc_io_sink_t       *sink;
+  int                 retval, mpiret;
+  p4est_t            *savecolumns;
+#ifdef P4EST_MPIIO_WRITE
+  MPI_File            mpifile;
+  MPI_Offset          mpipos;
+  MPI_Offset          mpithis;
+#else
+  long                fthis;
+  sc_MPI_Status       mpistatus;
+#endif
+  int                 num_procs = p6est->mpisize;
+
+  P4EST_GLOBAL_PRODUCTION ("Into p6est_save\n");
+  p4est_log_indent_push ();
+
+  savecolumns = p4est_copy (p6est->columns, 0);
+  p4est_reset_data (savecolumns, 2 * sizeof (p4est_locidx_t), NULL, NULL);
+  size_t              comb_size, data_size = p6est->data_size;
+  size_t              zz, nlayers = p6est->layers->elem_count;
+  char               *lbuf, *bp;
+
+  if (!data_size) {
+    save_data = 0;
+  }
+  if (!save_data) {
+    data_size = 0;
+  }
+
+  comb_size = 2 * sizeof (p4est_qcoord_t) + data_size;
+
+  {
+    p4est_topidx_t      jt;
+    p4est_tree_t       *tree, *savetree;
+    sc_array_t         *tquadrants, *savetquadrants;
+    p4est_quadrant_t   *col, *savecol;
+    size_t              first, last;
+    p4est_locidx_t      lfirst, llast;
+
+    for (jt = p6est->columns->first_local_tree;
+         jt <= p6est->columns->last_local_tree; ++jt) {
+      tree = p4est_tree_array_index (p6est->columns->trees, jt);
+      savetree = p4est_tree_array_index (savecolumns->trees, jt);
+      tquadrants = &tree->quadrants;
+      savetquadrants = &savetree->quadrants;
+      for (zz = 0; zz < tquadrants->elem_count; ++zz) {
+        p4est_locidx_t     *savedata;
+        col = p4est_quadrant_array_index (tquadrants, zz);
+        savecol = p4est_quadrant_array_index (savetquadrants, zz);
+        P6EST_COLUMN_GET_RANGE (col, &first, &last);
+        lfirst = (p4est_locidx_t) first;
+        llast = (p4est_locidx_t) last;
+        savedata = (p4est_locidx_t *) savecol->p.user_data;
+        savedata[0] = lfirst;
+        savedata[1] = llast;
+      }
+    }
+  }
+
+  /* save the columns */
+  p4est_save_ext (filename, savecolumns, 1, save_partition);
+
+  p4est_destroy (savecolumns);
+
+  /* we need a barrier so that all files have finished writing in
+   * p4est_save_ext before we start writing additional data to the file */
+  mpiret = sc_MPI_Barrier (p6est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  if (rank == 0) {
+    file = fopen (filename, "ab");
+    SC_CHECK_ABORT (file != NULL, "file open");
+
+    /* align */
+    fpos = ftell (file);
+    SC_CHECK_ABORT (fpos > 0, "first file tell");
+    while (fpos % align != 0) {
+      retval = fputc ('\0', file);
+      SC_CHECK_ABORT (retval == 0, "first file align");
+      ++fpos;
+    }
+
+    /* write the p6est_connectivity extra data */
+    sink = sc_io_sink_new (SC_IO_TYPE_FILEFILE, SC_IO_MODE_APPEND,
+                           SC_IO_ENCODE_NONE, file);
+    SC_CHECK_ABORT (sink != NULL, "file sink");
+    retval = p6est_connectivity_extra_sink (p6est->connectivity, sink);
+    SC_CHECK_ABORT (retval == 0, "sink connectivity");
+    retval = sc_io_sink_destroy (sink);
+    SC_CHECK_ABORT (retval == 0, "destroy sink");
+
+    fpos = ftell (file);
+    SC_CHECK_ABORT (fpos > 0, "second file tell");
+    while (fpos % align != 0) {
+      retval = fputc ('\0', file);
+      SC_CHECK_ABORT (retval == 0, "second file align");
+      ++fpos;
+    }
+
+    /* write the data size */
+    u64a = (uint64_t) data_size;
+    sc_fwrite (&u64a, sizeof (uint64_t), 1, file, "write data size");
+
+    fpos = ftell (file);
+    SC_CHECK_ABORT (fpos > 0, "third file tell");
+    while (fpos % align != 0) {
+      retval = fputc ('\0', file);
+      SC_CHECK_ABORT (retval == 0, "third file align");
+      ++fpos;
+    }
+
+#ifdef P4EST_MPIIO_WRITE
+    /* We will close the sequential access to the file */
+    /* best attempt to flush file to disk */
+    retval = fflush (file);
+    SC_CHECK_ABORT (retval == 0, "file flush");
+#ifdef P4EST_HAVE_FSYNC
+    retval = fsync (fileno (file));
+    SC_CHECK_ABORT (retval == 0, "file fsync");
+#endif
+    retval = fclose (file);
+    SC_CHECK_ABORT (retval == 0, "file close");
+    file = NULL;
+#else
+    /* file is still open for sequential write mode */
+#endif
+  }
+  else {
+    file = NULL;
+  }
+
+#ifndef P4EST_MPIIO_WRITE
+  if (rank > 0) {
+    /* wait for sequential synchronization */
+    mpiret = sc_MPI_Recv (&fpos, 1, sc_MPI_LONG, rank - 1, P4EST_COMM_SAVE,
+                          p6est->mpicomm, &mpistatus);
+    SC_CHECK_MPI (mpiret);
+
+    /* open file after all previous processors have written to it */
+    file = fopen (filename, "rb+");
+    SC_CHECK_ABORT (file != NULL, "file open");
+  }
+#else
+  /* Every core opens the file in append mode */
+  mpiret = MPI_File_open (p6est->mpicomm, (char *) filename,
+                          MPI_MODE_WRONLY | MPI_MODE_APPEND |
+                          MPI_MODE_UNIQUE_OPEN, MPI_INFO_NULL, &mpifile);
+  SC_CHECK_MPI (mpiret);
+  mpiret = MPI_File_get_position (mpifile, &mpipos);
+  SC_CHECK_MPI (mpiret);
+#endif
+
+  if (rank > 0) {
+    /* seek to the beginning of this processor's storage */
+    foffset = (long) (p6est->global_first_layer[rank] * comb_size);
+
+#ifndef P4EST_MPIIO_WRITE
+    fthis = fpos + foffset;
+    retval = fseek (file, fthis, SEEK_SET);
+    SC_CHECK_ABORT (retval == 0, "seek data");
+#else
+    mpithis = mpipos + (MPI_Offset) foffset;
+    mpiret = MPI_File_seek (mpifile, mpithis, MPI_SEEK_SET);
+    SC_CHECK_MPI (mpiret);
+#endif
+  }
+
+  /* write layers and data interleaved */
+  bp = lbuf = P4EST_ALLOC (char, comb_size * nlayers);
+  for (zz = 0; zz < nlayers; zz++) {
+    p2est_quadrant_t   *layer;
+    p4est_qcoord_t     *qpos;
+
+    qpos = (p4est_locidx_t *) bp;
+
+    layer = p2est_quadrant_array_index (p6est->layers, zz);
+    *qpos++ = layer->z;
+    *qpos++ = (p4est_qcoord_t) layer->level;
+    if (save_data) {
+      memcpy (qpos, layer->p.user_data, data_size);
+    }
+    bp += comb_size;
+  }
+#ifndef P4EST_MPIIO_WRITE
+  sc_fwrite (lbuf, comb_size, nlayers, file, "write quadrants");
+#else
+  sc_mpi_write (mpifile, lbuf, comb_size * nlayers, MPI_BYTE,
+                "write quadrants");
+#endif
+  P4EST_FREE (lbuf);
+
+#ifndef P4EST_MPIIO_WRITE
+  /* best attempt to flush file to disk */
+  retval = fflush (file);
+  SC_CHECK_ABORT (retval == 0, "file flush");
+#ifdef P4EST_HAVE_FSYNC
+  retval = fsync (fileno (file));
+  SC_CHECK_ABORT (retval == 0, "file fsync");
+#endif
+  retval = fclose (file);
+  SC_CHECK_ABORT (retval == 0, "file close");
+  file = NULL;
+
+  /* initiate sequential synchronization */
+  if (rank < num_procs - 1) {
+    mpiret = sc_MPI_Send (&fpos, 1, sc_MPI_LONG, rank + 1, P4EST_COMM_SAVE,
+                          p6est->mpicomm);
+    SC_CHECK_MPI (mpiret);
+  }
+#else
+  mpiret = MPI_File_close (&mpifile);
+  SC_CHECK_MPI (mpiret);
+#endif
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done p6est_save\n");
+}
+
+p6est_t            *
+p6est_load (const char *filename, sc_MPI_Comm mpicomm, size_t data_size,
+            int load_data, void *user_pointer,
+            p6est_connectivity_t ** connectivity)
+{
+  return p6est_load_ext (filename, mpicomm, data_size, load_data,
+                         0, 0, user_pointer, connectivity);
+}
+
+p6est_t            *
+p6est_load_ext (const char *filename, sc_MPI_Comm mpicomm, size_t data_size,
+                int load_data, int autopartition, int broadcasthead,
+                void *user_pointer, p6est_connectivity_t ** connectivity)
+{
+  int                 align = 32;
+  int                 retval;
+  sc_io_source_t     *src;
+  p4est_t            *loadcolumns, *columns;
+  p4est_connectivity_t *conn4;
+  p6est_connectivity_t *conn;
+  p4est_topidx_t      jt;
+  p4est_tree_t       *tree, *loadtree;
+  p4est_quadrant_t   *col, *loadcol;
+  sc_array_t         *tquadrants, *loadtquadrants;
+  size_t              zz, first, last, zcount, zpadding;
+  p4est_locidx_t     *loaddata, lfirst, llast, nlayers;
+  p6est_t            *p6est;
+  size_t              save_data_size, comb_size;
+  uint64_t            u64a;
+  p4est_gloidx_t     *gfl;
+  int                 rank, mpisize;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into p6est_load %s\n", filename);
+  p4est_log_indent_push ();
+
+  /* open file on all processors */
+  src = sc_io_source_new (SC_IO_TYPE_FILENAME, SC_IO_ENCODE_NONE, filename);
+  SC_CHECK_ABORT (src != NULL, "file source");
+
+  loadcolumns = p4est_source_ext (src, mpicomm, 2 * sizeof (p4est_locidx_t),
+                                  1, autopartition, broadcasthead, NULL,
+                                  &conn4);
+  columns = p4est_copy (loadcolumns, 0);
+
+  nlayers = 0;
+  for (jt = columns->first_local_tree; jt <= columns->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (columns->trees, jt);
+    loadtree = p4est_tree_array_index (loadcolumns->trees, jt);
+    tquadrants = &tree->quadrants;
+    loadtquadrants = &loadtree->quadrants;
+    for (zz = 0; zz < tquadrants->elem_count; ++zz) {
+      col = p4est_quadrant_array_index (tquadrants, zz);
+      loadcol = p4est_quadrant_array_index (loadtquadrants, zz);
+      loaddata = (p4est_locidx_t *) loadcol->p.user_data;
+      lfirst = loaddata[0];
+      llast = loaddata[1];
+      first = (size_t) nlayers;
+      nlayers += llast - lfirst;
+      last = first + (size_t) (llast - lfirst);
+
+      P6EST_COLUMN_SET_RANGE (col, first, last);
+    }
+  }
+  columns->connectivity = conn4;
+  p4est_destroy (loadcolumns);
+
+  /* padding */
+  zcount = src->bytes_out;
+  zpadding = (align - zcount % align) % align;
+  retval = sc_io_source_read (src, NULL, zpadding, NULL);
+  SC_CHECK_ABORT (!retval, "source padding");
+
+  conn = p6est_connectivity_extra_source (columns->connectivity, src);
+  if (connectivity != NULL) {
+    *connectivity = conn;
+  }
+
+  /* padding */
+  zcount = src->bytes_out;
+  zpadding = (align - zcount % align) % align;
+  retval = sc_io_source_read (src, NULL, zpadding, NULL);
+  SC_CHECK_ABORT (!retval, "source padding");
+
+  /* read data size */
+  retval = sc_io_source_read (src, &u64a, sizeof (uint64_t), NULL);
+  SC_CHECK_ABORT (!retval, "source data size");
+  save_data_size = (size_t) u64a;
+
+  if (load_data) {
+    SC_CHECK_ABORT (save_data_size == data_size, "data size mismatch");
+  }
+
+  /* padding */
+  zcount = src->bytes_out;
+  zpadding = (align - zcount % align) % align;
+  retval = sc_io_source_read (src, NULL, zpadding, NULL);
+  SC_CHECK_ABORT (!retval, "source padding");
+
+  p6est = P4EST_ALLOC (p6est_t, 1);
+  columns->user_pointer = p6est;
+
+  p6est->columns = columns;
+  p6est->connectivity = conn;
+  p6est->data_size = data_size;
+  p6est->mpicomm = mpicomm;
+  p6est->mpisize = mpisize = columns->mpisize;
+  p6est->mpirank = rank = columns->mpirank;
+  p6est->global_first_layer = gfl = P4EST_ALLOC (p4est_gloidx_t,
+                                                 p6est->mpisize + 1);
+  p6est->layers =
+    sc_array_new_size (sizeof (p2est_quadrant_t), (size_t) nlayers);
+  p6est->layer_pool = sc_mempool_new (sizeof (p2est_quadrant_t));
+  p6est->user_pointer = user_pointer;
+  p6est->user_data_pool = data_size ? sc_mempool_new (data_size) : NULL;
+
+  p6est_update_offsets (p6est);
+
+  comb_size = 2 * sizeof (p4est_qcoord_t) + save_data_size;
+
+  if (gfl[rank]) {
+    /* seek to the start of my layers */
+    retval = sc_io_source_read (src, NULL, (long) (gfl[rank] * comb_size),
+                                NULL);
+    SC_CHECK_ABORT (!retval, "seek to layers");
+  }
+
+  if (nlayers) {
+    sc_array_t         *loaddata;
+
+    loaddata = sc_array_new_size (comb_size, (size_t) nlayers);
+
+    retval =
+      sc_io_source_read (src, loaddata->array, comb_size * nlayers, NULL);
+    SC_CHECK_ABORT (!retval, "read layers");
+
+    for (zz = 0; zz < (size_t) nlayers; zz++) {
+      p2est_quadrant_t   *layer =
+        p2est_quadrant_array_index (p6est->layers, zz);
+      p4est_qcoord_t     *loadlayer =
+        (p4est_qcoord_t *) sc_array_index (loaddata, zz);
+
+      P2EST_QUADRANT_INIT (layer);
+      layer->z = loadlayer[0];
+      layer->level = (int8_t) loadlayer[1];
+      if (load_data) {
+        layer->p.user_data = sc_mempool_alloc (p6est->user_data_pool);
+        memcpy (layer->p.user_data, &loadlayer[2], save_data_size);
+      }
+    }
+    sc_array_destroy (loaddata);
+  }
+
+  retval = sc_io_source_destroy (src);
+  SC_CHECK_ABORT (!retval, "destroy source");
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done p6est_load %s\n", filename);
+
+  return p6est;
+}
+
+typedef struct p6est_refine_col_data
+{
+  p6est_refine_column_t refine_col_fn;
+  p6est_init_t        init_fn;
+  p6est_replace_t     replace_fn;
+  void               *user_pointer;
+}
+p6est_refine_col_data_t;
+
+static int
+p6est_refine_column_int (p4est_t * p4est, p4est_topidx_t which_tree,
+                         p4est_quadrant_t * quadrant)
+{
+  p6est_t            *p6est = (p6est_t *) p4est->user_pointer;
+  p6est_refine_col_data_t *refine_col =
+    (p6est_refine_col_data_t *) p6est->user_pointer;
+  int                 retval;
+
+  /* sneak the user pointer in */
+  p6est->user_pointer = refine_col->user_pointer;
+  retval = refine_col->refine_col_fn (p6est, which_tree, quadrant);
+
+  /* sneak the user pointer_out */
+  p6est->user_pointer = (void *) refine_col;
+
+  return retval;
+}
+
+static void
+p6est_replace_column_split (p4est_t * p4est, p4est_topidx_t which_tree,
+                            int num_outgoing, p4est_quadrant_t * outgoing[],
+                            int num_incoming, p4est_quadrant_t * incoming[])
+{
+  p6est_t            *p6est = (p6est_t *) p4est->user_pointer;
+  p6est_refine_col_data_t *refine_col =
+    (p6est_refine_col_data_t *) p6est->user_pointer;
+  int                 nlayers;
+  size_t              first, last, ifirst, ilast;
+  int                 i, j;
+  p2est_quadrant_t   *oq, *q;
+
+  /* sneak the user pointer in */
+  p6est->user_pointer = refine_col->user_pointer;
+  P4EST_ASSERT (num_outgoing == 1);
+  P4EST_ASSERT (num_incoming == P4EST_CHILDREN);
+
+  P6EST_COLUMN_GET_RANGE (outgoing[0], &first, &last);
+  nlayers = last - first;
+
+  for (i = 0; i < num_incoming; i++) {
+    ifirst = p6est->layers->elem_count;
+    ilast = ifirst + nlayers;
+    q = (p2est_quadrant_t *) sc_array_push_count (p6est->layers, nlayers);
+    oq = (p2est_quadrant_t *) sc_array_index (p6est->layers, first);
+    P6EST_COLUMN_SET_RANGE (incoming[i], ifirst, ilast);
+    for (j = 0; j < nlayers; j++, oq++, q++) {
+      P2EST_QUADRANT_INIT (q);
+      q->z = oq->z;
+      q->level = oq->level;
+      p6est_layer_init_data (p6est, which_tree, incoming[i], q,
+                             refine_col->init_fn);
+    }
+  }
+
+  if (refine_col->replace_fn != NULL) {
+    for (j = 0; j < nlayers; j++) {
+      p2est_quadrant_t   *inq[P4EST_CHILDREN];
+
+      oq = p2est_quadrant_array_index (p6est->layers, first + j);
+      for (i = 0; i < P4EST_CHILDREN; i++) {
+        P6EST_COLUMN_GET_RANGE (incoming[i], &ifirst, &ilast);
+        inq[i] = p2est_quadrant_array_index (p6est->layers, ifirst + j);
+      }
+
+      refine_col->replace_fn (p6est, which_tree,
+                              1, 1,
+                              outgoing, &oq,
+                              P4EST_CHILDREN, P4EST_CHILDREN, incoming, inq);
+    }
+  }
+
+  for (j = 0; j < nlayers; j++) {
+    oq = p2est_quadrant_array_index (p6est->layers, first + j);
+    p6est_layer_free_data (p6est, oq);
+  }
+  /* sneak the user pointer out */
+  p6est->user_pointer = (void *) refine_col;
+}
+
+void
+p6est_compress_columns (p6est_t * p6est)
+{
+  size_t              zz, zy, first, last;
+  p4est_topidx_t      jt;
+  p4est_quadrant_t   *col;
+  p4est_tree_t       *tree;
+  sc_array_t         *tquadrants;
+  size_t              offset, nkeep;
+  int                 count;
+  size_t              this_col;
+  p4est_t            *columns = p6est->columns;
+  sc_array_t         *layers = p6est->layers;
+  size_t             *newindex;
+  sc_array_t         *na;
+  size_t              nlayers = layers->elem_count;
+
+  na = sc_array_new_size (sizeof (size_t), nlayers);
+  newindex = (size_t *) na->array;
+  for (zy = 0; zy < nlayers; zy++) {
+    newindex[zy] = nlayers;
+  }
+
+  offset = 0;
+  for (this_col = 0, jt = columns->first_local_tree;
+       jt <= columns->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (columns->trees, jt);
+    tquadrants = &tree->quadrants;
+    for (zz = 0; zz < tquadrants->elem_count; ++zz, this_col++) {
+      col = p4est_quadrant_array_index (tquadrants, zz);
+      P6EST_COLUMN_GET_RANGE (col, &first, &last);
+      count = last - first;
+      P4EST_ASSERT (count > 0);
+      P6EST_COLUMN_SET_RANGE (col, offset, offset + count);
+      for (zy = first; zy < last; zy++) {
+        newindex[zy] = offset++;
+      }
+    }
+  }
+  nkeep = offset;
+  P4EST_ASSERT (nkeep <= nlayers);
+
+  for (zy = 0; zy < nlayers; zy++) {
+    if (newindex[zy] == nlayers) {
+      newindex[zy] = offset++;
+    }
+  }
+  P4EST_ASSERT (offset == nlayers);
+
+  sc_array_permute (layers, na, 0);
+  sc_array_resize (p6est->layers, nkeep);
+  sc_array_destroy (na);
+}
+
+void
+p6est_update_offsets (p6est_t * p6est)
+{
+  int                 p, mpiret;
+  p4est_gloidx_t     *gfl = p6est->global_first_layer;
+  p4est_gloidx_t      mycount = p6est->layers->elem_count;
+  p4est_gloidx_t      psum = 0, thiscount;
+
+  mpiret = sc_MPI_Allgather (&mycount, 1, P4EST_MPI_GLOIDX, gfl, 1,
+                             P4EST_MPI_GLOIDX, p6est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  for (p = 0; p < p6est->mpisize; p++) {
+    thiscount = gfl[p];
+    gfl[p] = psum;
+    psum += thiscount;
+  }
+  gfl[p6est->mpisize] = psum;
+  P4EST_ASSERT ((size_t) (gfl[p6est->mpirank + 1] - gfl[p6est->mpirank]) ==
+                p6est->layers->elem_count);
+}
+
+void
+p6est_refine_columns_ext (p6est_t * p6est, int refine_recursive,
+                          int allowed_level, p6est_refine_column_t refine_fn,
+                          p6est_init_t init_fn, p6est_replace_t replace_fn)
+{
+  p6est_refine_col_data_t refine_col;
+  void               *orig_user_pointer = p6est->user_pointer;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into p6est_refine_columns with %lld total layers"
+                            " in %lld total columns\n",
+                            (long long) p6est->global_first_layer[p6est->
+                                                                  mpisize],
+                            (long long) p6est->columns->global_num_quadrants);
+  p4est_log_indent_push ();
+  refine_col.refine_col_fn = refine_fn;
+  refine_col.init_fn = init_fn;
+  refine_col.replace_fn = replace_fn;
+  refine_col.user_pointer = orig_user_pointer;
+
+  p6est->user_pointer = (void *) &refine_col;
+  P4EST_GLOBAL_VERBOSE ("Refining p4est for columns\n");
+  p4est_refine_ext (p6est->columns, refine_recursive, allowed_level,
+                    p6est_refine_column_int, NULL,
+                    p6est_replace_column_split);
+  p6est->user_pointer = orig_user_pointer;
+
+  p6est_compress_columns (p6est);
+  p6est_update_offsets (p6est);
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done p6est_refine_columns with %lld total layers"
+                            " in %lld total columns\n",
+                            (long long) p6est->global_first_layer[p6est->
+                                                                  mpisize],
+                            (long long) p6est->columns->global_num_quadrants);
+}
+
+void
+p6est_refine_layers_ext (p6est_t * p6est, int refine_recursive,
+                         int allowed_level, p6est_refine_layer_t refine_fn,
+                         p6est_init_t init_fn, p6est_replace_t replace_fn)
+{
+  p4est_t            *columns = p6est->columns;
+  sc_array_t         *layers = p6est->layers;
+  sc_array_t         *newcol = sc_array_new (sizeof (p2est_quadrant_t));
+  p4est_topidx_t      jt;
+  p4est_tree_t       *tree;
+  sc_array_t         *tquadrants;
+  p4est_quadrant_t   *col;
+  p2est_quadrant_t   *q, *newq;
+  p2est_quadrant_t    nextq[P4EST_MAXLEVEL];
+  p2est_quadrant_t    c[2];
+  p2est_quadrant_t    p, *parent = &p;
+  p2est_quadrant_t   *child[2];
+  size_t              first, last, zz, current, old_count;
+  int                 any_change;
+  int                 level;
+  int                 stop_recurse;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into p6est_refine_layers with %lld total layers"
+                            " in %lld total columns, allowed level %d\n",
+                            (long long) p6est->global_first_layer[p6est->
+                                                                  mpisize],
+                            (long long) p6est->columns->global_num_quadrants,
+                            allowed_level);
+  p4est_log_indent_push ();
+  for (jt = columns->first_local_tree; jt <= columns->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (columns->trees, jt);
+    tquadrants = &tree->quadrants;
+
+    for (zz = 0; zz < tquadrants->elem_count; ++zz) {
+      col = p4est_quadrant_array_index (tquadrants, zz);
+      P6EST_COLUMN_GET_RANGE (col, &first, &last);
+
+      any_change = 0;
+
+      for (current = first; current < last; current++) {
+        q = p2est_quadrant_array_index (layers, current);
+        stop_recurse = 0;
+        level = q->level;
+        parent = q;
+        for (;;) {
+          if (!stop_recurse && refine_fn (p6est, jt, col, parent) &&
+              (allowed_level < 0 || (int) parent->level < allowed_level)) {
+            level++;
+            any_change = 1;
+            c[0] = *parent;
+            c[0].level = level;
+            c[1] = *parent;
+            c[1].level = level;
+            c[1].z += P4EST_QUADRANT_LEN (level);
+            child[0] = &c[0];
+            child[1] = &c[1];
+            p6est_layer_init_data (p6est, jt, col, child[0], init_fn);
+            p6est_layer_init_data (p6est, jt, col, child[1], init_fn);
+            if (replace_fn != NULL) {
+              replace_fn (p6est, jt, 1, 1, &col, &parent, 1, 2, &col, child);
+            }
+            p6est_layer_free_data (p6est, parent);
+            p = c[0];
+            parent = &p;
+            nextq[level] = c[1];
+            stop_recurse = !refine_recursive;
+          }
+          else {
+            /* parent is accepted */
+            newq = (p2est_quadrant_t *) sc_array_push (newcol);
+            *newq = *parent;
+            if (parent == &p) {
+              parent = &nextq[level];
+            }
+            else {
+              while (--level > q->level && parent->z > nextq[level].z) {
+              }
+              if (level <= q->level) {
+                break;
+              }
+              parent = &(nextq[level]);
+            }
+          }
+        }
+      }
+      if (any_change) {
+        old_count = layers->elem_count;
+        newq = (p2est_quadrant_t *) sc_array_push_count (layers,
+                                                         newcol->elem_count);
+        memcpy (newq, sc_array_index (newcol, 0),
+                newcol->elem_size * newcol->elem_count);
+        P6EST_COLUMN_SET_RANGE (col, old_count,
+                                old_count + newcol->elem_count);
+      }
+      sc_array_truncate (newcol);
+    }
+  }
+  sc_array_destroy (newcol);
+  p6est_compress_columns (p6est);
+  p6est_update_offsets (p6est);
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done p6est_refine_layers with %lld total layers "
+                            " in %lld total columns\n",
+                            (long long) p6est->global_first_layer[p6est->
+                                                                  mpisize],
+                            (long long) p6est->columns->global_num_quadrants);
+}
+
+void
+p6est_refine_columns (p6est_t * p6est, int refine_recursive,
+                      p6est_refine_column_t refine_fn, p6est_init_t init_fn)
+{
+  p6est_refine_columns_ext (p6est, refine_recursive, -1, refine_fn, init_fn,
+                            NULL);
+}
+
+void
+p6est_refine_layers (p6est_t * p6est, int refine_recursive,
+                     p6est_refine_layer_t refine_fn, p6est_init_t init_fn)
+{
+  p6est_refine_layers_ext (p6est, refine_recursive, -1, refine_fn, init_fn,
+                           NULL);
+}
+
+typedef struct p6est_coarsen_col_data
+{
+  p6est_coarsen_column_t coarsen_col_fn;
+  p6est_init_t        init_fn;
+  p6est_replace_t     replace_fn;
+  void               *user_pointer;
+  sc_array_t         *work_array;       /* for merging columns */
+}
+p6est_coarsen_col_data_t;
+
+static int
+p6est_coarsen_column_int (p4est_t * p4est, p4est_topidx_t which_tree,
+                          p4est_quadrant_t * quadrants[])
+{
+  p6est_t            *p6est = (p6est_t *) p4est->user_pointer;
+  p6est_coarsen_col_data_t *coarsen_col =
+    (p6est_coarsen_col_data_t *) p6est->user_pointer;
+  int                 retval;
+
+  /* sneak the user pointer in */
+  p6est->user_pointer = coarsen_col->user_pointer;
+  retval = coarsen_col->coarsen_col_fn (p6est, which_tree, quadrants);
+
+  /* sneak the user pointer_out */
+  p6est->user_pointer = (void *) coarsen_col;
+
+  return retval;
+}
+
+static int
+p2est_quadrant_is_equal (p2est_quadrant_t * a, p2est_quadrant_t * b)
+{
+  return (a->z == b->z && a->level == b->level);
+}
+
+static int
+p2est_quadrant_is_ancestor (p2est_quadrant_t * a, p2est_quadrant_t * b)
+{
+  if (a->level >= b->level) {
+    return 0;
+  }
+
+  return (b->z >= a->z && b->z < a->z + P4EST_QUADRANT_LEN (a->level));
+}
+
+static void
+p6est_coarsen_all_layers (p6est_t * p6est, p4est_topidx_t which_tree,
+                          p4est_quadrant_t * column, int ancestor_level,
+                          sc_array_t * descendants,
+                          int coarsen_recursive,
+                          int callback_orphan,
+                          p6est_coarsen_layer_t coarsen_fn,
+                          p6est_init_t init_fn, p6est_replace_t replace_fn)
+{
+  p2est_quadrant_t    prevq[P4EST_QMAXLEVEL];
+  size_t              old_count = descendants->elem_count;
+  size_t              zz, new_count = 0;
+  p2est_quadrant_t   *q, *r, a, s, *sib[2];
+  int                 i, stackheight;
+#ifdef P4EST_DEBUG
+  size_t              mcount = p6est->user_data_pool->elem_count;
+  p4est_qcoord_t      startpos, endpos;
+#endif
+
+  P4EST_ASSERT (old_count > 0);
+
+  q = p2est_quadrant_array_index (descendants, 0);
+#ifdef P4EST_DEBUG
+  startpos = q->z;
+#endif
+
+  if (q->level == ancestor_level) {
+    P4EST_ASSERT (old_count == 1);
+    if (coarsen_fn != NULL && callback_orphan) {
+      sib[0] = q;
+      sib[1] = NULL;
+      coarsen_fn (p6est, which_tree, column, sib);
+    }
+    return;
+  }
+
+  P4EST_ASSERT (old_count > 1);
+
+  /* the stack contains only first siblings until it is time to move the stack
+   * back to the array */
+  P4EST_ASSERT (!(q->z & P4EST_QUADRANT_LEN (q->level)));
+
+  stackheight = 0;
+  zz = 1;
+  for (;;) {
+    /* if this is a second sibling */
+    if (q->z & P4EST_QUADRANT_LEN (q->level)) {
+      if (!stackheight) {
+        prevq[stackheight++] = *q;
+      }
+      else {
+        /* pop the first sibling off the stack */
+        sib[0] = &prevq[stackheight - 1];
+        s = *q;
+        sib[1] = &s;
+        P4EST_ASSERT (sib[0]->level == sib[1]->level);
+        P4EST_ASSERT (sib[0]->z + P4EST_QUADRANT_LEN (sib[0]->level) ==
+                      sib[1]->z);
+        /* test if we will coarsen this pair */
+        if (coarsen_fn == NULL || coarsen_fn (p6est, which_tree, column, sib)) {
+          /* coarsen */
+          a = *sib[0];
+          q = &a;
+          q->level--;
+          P4EST_ASSERT (p2est_quadrant_is_ancestor (q, sib[0]));
+          P4EST_ASSERT (p2est_quadrant_is_ancestor (q, sib[1]));
+          p6est_layer_init_data (p6est, which_tree, column, q, init_fn);
+          if (replace_fn) {
+            replace_fn (p6est, which_tree, 1, 2, &column, sib, 1, 1, &column,
+                        &q);
+          }
+          p6est_layer_free_data (p6est, sib[0]);
+          p6est_layer_free_data (p6est, sib[1]);
+          /* if we are doing recursive coarsening, do not advance */
+          if (coarsen_recursive) {
+            stackheight--;
+          }
+          else {
+            /* put the parent on the end of the stack */
+            prevq[stackheight - 1] = *q;
+          }
+        }
+        else {
+          prevq[stackheight++] = *q;
+        }
+      }
+    }
+    else {
+      prevq[stackheight++] = *q;
+      if (q->level > ancestor_level) {
+        P4EST_ASSERT (zz < old_count);
+        q = p2est_quadrant_array_index (descendants, zz++);
+      }
+    }
+
+    if (stackheight && p2est_quadrant_is_equal (&prevq[stackheight - 1], q)) {
+      /* clear the stack into the array from the bottom up */
+      for (i = 0; i < stackheight; i++) {
+        r = p2est_quadrant_array_index (descendants, new_count++);
+        *r = prevq[i];
+        if (coarsen_fn != NULL && callback_orphan) {
+          sib[0] = r;
+          sib[1] = NULL;
+          coarsen_fn (p6est, which_tree, column, sib);
+        }
+      }
+      stackheight = 0;
+      if (zz == old_count) {
+        break;
+      }
+      else {
+        q = p2est_quadrant_array_index (descendants, zz++);
+      }
+    }
+  }
+
+  sc_array_resize (descendants, new_count);
+
+#ifdef P4EST_DEBUG
+  P4EST_ASSERT (mcount - p6est->user_data_pool->elem_count ==
+                (old_count - new_count));
+
+  q = p2est_quadrant_array_index (descendants, new_count - 1);
+  endpos = q->z + P4EST_QUADRANT_LEN (q->level);
+  P4EST_ASSERT (endpos - startpos == P4EST_QUADRANT_LEN (ancestor_level));
+
+#endif
+}
+
+static void
+p6est_replace_column_join (p4est_t * p4est, p4est_topidx_t which_tree,
+                           int num_outgoing, p4est_quadrant_t * outgoing[],
+                           int num_incoming, p4est_quadrant_t * incoming[])
+{
+  p6est_t            *p6est = (p6est_t *) p4est->user_pointer;
+  p6est_coarsen_col_data_t *coarsen_col =
+    (p6est_coarsen_col_data_t *) p6est->user_pointer;
+  size_t              nlayers[P4EST_CHILDREN];
+  size_t              first[P4EST_CHILDREN], last[P4EST_CHILDREN];
+  size_t              zw[P4EST_CHILDREN];
+  size_t              view_first, view_count;
+  size_t              new_first, new_count, new_last;
+  int                 j;
+  p2est_quadrant_t   *q[P4EST_CHILDREN], *p;
+  sc_array_t         *layers = p6est->layers;
+  sc_array_t         *work_array = coarsen_col->work_array;
+  sc_array_t          view;
+  p6est_init_t        init_fn = coarsen_col->init_fn;
+  p6est_replace_t     replace_fn = coarsen_col->replace_fn;
+
+  /* sneak the user pointer in */
+  p6est->user_pointer = coarsen_col->user_pointer;
+
+  P4EST_ASSERT (num_outgoing == P4EST_CHILDREN);
+  P4EST_ASSERT (num_incoming == 1);
+  P4EST_ASSERT (!work_array->elem_count);
+
+  new_first = layers->elem_count;
+  new_count = 0;
+
+  /* zero counters for each column */
+  for (j = 0; j < num_outgoing; j++) {
+    zw[j] = 0;
+    P6EST_COLUMN_GET_RANGE (outgoing[j], &first[j], &last[j]);
+    nlayers[j] = last[j] - first[j];
+  }
+
+  while (zw[0] < nlayers[0]) {
+    for (j = 0; j < num_outgoing; j++) {
+      P4EST_ASSERT (zw[j] < nlayers[j]);
+      q[j] = p2est_quadrant_array_index (layers, first[j] + zw[j]);
+    }
+    p = (p2est_quadrant_t *) sc_array_push (work_array);
+    *p = *q[0];
+    p6est_layer_init_data (p6est, which_tree, incoming[0], p, init_fn);
+    for (j = 1; j < num_outgoing; j++) {
+      P4EST_ASSERT (q[j]->z == p->z);
+      if (q[j]->level < p->level) {
+        *p = *q[j];
+      }
+    }
+    for (j = 0; j < num_outgoing; j++) {
+      if (q[j]->level > p->level) {
+        P4EST_ASSERT (p2est_quadrant_is_ancestor (p, q[j]));
+        view_count = 1;
+        view_first = first[j] + zw[j];
+        P4EST_ASSERT (zw[j] < nlayers[j] - 1);
+        while (++zw[j] < nlayers[j] &&
+               p2est_quadrant_is_ancestor (p,
+                                           p2est_quadrant_array_index (layers,
+                                                                       first
+                                                                       [j] +
+                                                                       zw
+                                                                       [j])))
+        {
+          view_count++;
+        }
+        sc_array_init_view (&view, layers, view_first, view_count);
+        /* coarsen within this column */
+        p6est_coarsen_all_layers (p6est, which_tree, outgoing[j], p->level,
+                                  &view, 1, 0, NULL, init_fn, replace_fn);
+        q[j] = p2est_quadrant_array_index (&view, 0);
+      }
+      else {
+        zw[j]++;
+      }
+    }
+    if (replace_fn != NULL) {
+      replace_fn (p6est, which_tree, P4EST_CHILDREN, 1, outgoing, q, 1, 1,
+                  incoming, &p);
+    }
+    for (j = 0; j < num_outgoing; j++) {
+      p6est_layer_free_data (p6est, q[j]);
+    }
+  }
+
+  new_count = work_array->elem_count;
+  new_last = new_first + new_count;
+  P6EST_COLUMN_SET_RANGE (incoming[0], new_first, new_last);
+
+  /* create the new column */
+  p = (p2est_quadrant_t *) sc_array_push_count (layers, new_count);
+  memcpy (p, sc_array_index (work_array, 0),
+          new_count * work_array->elem_size);
+
+  sc_array_truncate (work_array);
+  /* sneak the user pointer out */
+  p6est->user_pointer = (void *) coarsen_col;
+}
+
+void
+p6est_coarsen_columns_ext (p6est_t * p6est, int coarsen_recursive,
+                           int callback_orphans,
+                           p6est_coarsen_column_t coarsen_fn,
+                           p6est_init_t init_fn, p6est_replace_t replace_fn)
+{
+  p6est_coarsen_col_data_t coarsen_col;
+  void               *orig_user_pointer = p6est->user_pointer;
+
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Into p6est_coarsen_columns with %lld total layers"
+     " in %lld total columns\n",
+     (long long) p6est->global_first_layer[p6est->mpisize],
+     (long long) p6est->columns->global_num_quadrants);
+  p4est_log_indent_push ();
+
+  coarsen_col.coarsen_col_fn = coarsen_fn;
+  coarsen_col.init_fn = init_fn;
+  coarsen_col.replace_fn = replace_fn;
+  coarsen_col.user_pointer = orig_user_pointer;
+  coarsen_col.work_array = sc_array_new (sizeof (p2est_quadrant_t));
+
+  p6est->user_pointer = (void *) &coarsen_col;
+  p4est_coarsen_ext (p6est->columns, coarsen_recursive, callback_orphans,
+                     p6est_coarsen_column_int, NULL,
+                     p6est_replace_column_join);
+  p6est->user_pointer = orig_user_pointer;
+
+  sc_array_destroy (coarsen_col.work_array);
+  p6est_compress_columns (p6est);
+  p6est_update_offsets (p6est);
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Done p6est_coarsen_columns with %lld total layers "
+     " in %lld total columns\n",
+     (long long) p6est->global_first_layer[p6est->mpisize],
+     (long long) p6est->columns->global_num_quadrants);
+}
+
+void
+p6est_coarsen_columns (p6est_t * p6est, int coarsen_recursive,
+                       p6est_coarsen_column_t coarsen_fn,
+                       p6est_init_t init_fn)
+{
+  p6est_coarsen_columns_ext (p6est, coarsen_recursive, 0,
+                             coarsen_fn, init_fn, NULL);
+}
+
+void
+p6est_coarsen_layers_ext (p6est_t * p6est, int coarsen_recursive,
+                          int callback_orphans,
+                          p6est_coarsen_layer_t coarsen_fn,
+                          p6est_init_t init_fn, p6est_replace_t replace_fn)
+{
+  p4est_t            *columns = p6est->columns;
+  sc_array_t         *layers = p6est->layers;
+  sc_array_t          view;
+  p4est_topidx_t      jt;
+  p4est_tree_t       *tree;
+  sc_array_t         *tquadrants;
+  p4est_quadrant_t   *col;
+  size_t              first, last, zz, count;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into p6est_coarsen_layers with %lld total layers"
+                            " in %lld total columns\n",
+                            (long long) p6est->global_first_layer[p6est->
+                                                                  mpisize],
+                            (long long) p6est->columns->global_num_quadrants);
+  p4est_log_indent_push ();
+
+  for (jt = columns->first_local_tree; jt <= columns->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (columns->trees, jt);
+    tquadrants = &tree->quadrants;
+
+    for (zz = 0; zz < tquadrants->elem_count; ++zz) {
+      col = p4est_quadrant_array_index (tquadrants, zz);
+      P6EST_COLUMN_GET_RANGE (col, &first, &last);
+
+      count = last - first;
+      sc_array_init_view (&view, layers, first, count);
+      p6est_coarsen_all_layers (p6est, jt, col, 0, &view,
+                                coarsen_recursive, callback_orphans,
+                                coarsen_fn, init_fn, replace_fn);
+      P4EST_ASSERT (view.elem_count > 0);
+      P4EST_ASSERT (view.elem_count <= count);
+      last = first + view.elem_count;
+      P6EST_COLUMN_SET_RANGE (col, first, last);
+    }
+  }
+  p6est_compress_columns (p6est);
+  p6est_update_offsets (p6est);
+  P4EST_ASSERT (p6est->user_data_pool->elem_count == layers->elem_count);
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Done p6est_coarsen_layers with %lld total layers "
+     " in %lld total columns\n",
+     (long long) p6est->global_first_layer[p6est->mpisize],
+     (long long) p6est->columns->global_num_quadrants);
+}
+
+void
+p6est_coarsen_layers (p6est_t * p6est, int coarsen_recursive,
+                      p6est_coarsen_layer_t coarsen_fn, p6est_init_t init_fn)
+{
+  p6est_coarsen_layers_ext (p6est, coarsen_recursive, 0, coarsen_fn, init_fn,
+                            NULL);
+}
+
+typedef struct p6est_weight_column
+{
+  p6est_weight_t      layer_weight_fn;
+  void               *user_pointer;
+}
+p6est_weight_column_t;
+
+static int
+p6est_weight_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+                 p4est_quadrant_t * q)
+{
+  p6est_t            *p6est = (p6est_t *) p4est->user_pointer;
+  p6est_weight_column_t *wc = (p6est_weight_column_t *) p6est->user_pointer;
+  void               *orig_pointer = wc->user_pointer;
+  size_t              first, last, zz;
+  int                 weight = 0;
+
+  p6est->user_pointer = orig_pointer;
+
+  P6EST_COLUMN_GET_RANGE (q, &first, &last);
+
+  if (wc->layer_weight_fn == NULL) {
+    weight = last - first;
+  }
+  else {
+    for (zz = first; zz < last; zz++) {
+      p2est_quadrant_t   *layer =
+        p2est_quadrant_array_index (p6est->layers, zz);
+
+      weight += wc->layer_weight_fn (p6est, which_tree, q, layer);
+    }
+  }
+
+  p6est->user_pointer = (void *) wc;
+
+  return weight;
+}
+
+static int
+gloidx_compare_overlap (const void *key, const void *array)
+{
+  const p4est_gloidx_t search = *((const p4est_gloidx_t *) key);
+  const p4est_gloidx_t *range = (const p4est_gloidx_t *) array;
+
+  if (search >= range[0]) {
+    if (search < range[1]) {
+      return 0;
+    }
+    else {
+      return 1;
+    }
+  }
+  else {
+    return -1;
+  }
+}
+
+p4est_gloidx_t
+p6est_partition_ext (p6est_t * p6est, int partition_for_coarsening,
+                     p6est_weight_t weight_fn)
+{
+  p6est_weight_column_t wc;
+  size_t              zz, offset, count, first, last;
+  p4est_gloidx_t      my_count;
+  p4est_gloidx_t     *new_gfl, *old_gfl = p6est->global_first_layer;
+  p4est_topidx_t      jt;
+  p4est_tree_t       *tree;
+  p4est_t            *columns = p6est->columns;
+  p4est_quadrant_t   *col;
+  sc_array_t         *tquadrants;
+  sc_array_t          new_gfl_bsearch, old_gfl_bsearch;
+  int                 mpiret, p, rank = p6est->mpirank;
+  p4est_gloidx_t      psum, thiscount;
+  ssize_t             search;
+  sc_array_t         *recv, *send;
+  size_t              data_size = p6est->data_size;
+  int                 overlap;
+  int                 mpisize = p6est->mpisize;
+  int                 send_count, recv_count;
+  sc_array_t         *send_requests;
+  sc_array_t         *recv_requests;
+  sc_MPI_Request     *req;
+  void               *layer_data;
+  p2est_quadrant_t   *layer;
+  p2est_quadrant_t   *packedlayer;
+  int                 i, nrecv, nsend, nleft;
+  int                 outcount, *array_of_indices;
+  size_t              self_offset, self_count;
+  sc_array_t         *new_layers;
+  p4est_gloidx_t      local_offset, local_last;
+  sc_array_t         *old_layers = p6est->layers;
+  sc_array_t         *recv_procs;
+  p4est_gloidx_t      shipped;
+  int                *ip;
+  void               *orig_user_pointer = p6est->user_pointer;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into p6est_parition with %lld total layers"
+                            " in %lld total columns\n",
+                            (long long) p6est->global_first_layer[p6est->
+                                                                  mpisize],
+                            (long long) p6est->columns->global_num_quadrants);
+  p4est_log_indent_push ();
+  /* wrap the p6est_weight_t in a p4est_weight_t */
+  wc.layer_weight_fn = weight_fn;
+  wc.user_pointer = orig_user_pointer;
+  p6est->user_pointer = &wc;
+  p6est_compress_columns (p6est);
+  /* repartition the columns */
+  p4est_partition_ext (p6est->columns, partition_for_coarsening,
+                       p6est_weight_fn);
+  p6est->user_pointer = orig_user_pointer;
+
+  /* the column counts (last - first) are correct, but not the offsets. update
+   * the offsets */
+  offset = 0;
+  for (jt = columns->first_local_tree; jt <= columns->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (columns->trees, jt);
+    tquadrants = &tree->quadrants;
+
+    for (zz = 0; zz < tquadrants->elem_count; ++zz) {
+      col = p4est_quadrant_array_index (tquadrants, zz);
+      P6EST_COLUMN_GET_RANGE (col, &first, &last);
+      count = last - first;
+      P4EST_ASSERT (count > 0);
+      first = offset;
+      last = offset + count;
+      offset += count;
+      P6EST_COLUMN_SET_RANGE (col, first, last);
+    }
+  }
+  my_count = (p4est_gloidx_t) offset;
+
+  /* calculate the new global_first_layer */
+  new_gfl = P4EST_ALLOC (p4est_gloidx_t, p6est->mpisize + 1);
+  mpiret = sc_MPI_Allgather (&my_count, 1, P4EST_MPI_GLOIDX, new_gfl, 1,
+                             P4EST_MPI_GLOIDX, p6est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  psum = 0;
+  for (p = 0; p < p6est->mpisize; p++) {
+    thiscount = new_gfl[p];
+    new_gfl[p] = psum;
+    psum += thiscount;
+  }
+  new_gfl[p6est->mpisize] = psum;
+  P4EST_ASSERT (new_gfl[p6est->mpisize] == old_gfl[p6est->mpisize]);
+
+  /* calculate the global number of shipped (received) layers */
+  shipped = 0;
+  for (i = 0; i < mpisize; i++) {
+    p4est_locidx_t      new_total, same, diff;
+
+    new_total = new_gfl[i + 1] - new_gfl[i];
+    same =
+      SC_MIN (new_gfl[i + 1], old_gfl[i + 1]) - SC_MAX (new_gfl[i],
+                                                        old_gfl[i]);
+    diff = new_total - SC_MAX (0, same);
+    shipped += diff;
+  }
+
+  if (old_gfl[rank] == new_gfl[rank] &&
+      old_gfl[rank + 1] == new_gfl[rank + 1]) {
+    /* if my range is unchanged, I neither send nor receive to anyone */
+    p6est->global_first_layer = new_gfl;
+    P4EST_FREE (old_gfl);
+    p4est_log_indent_pop ();
+    P4EST_GLOBAL_PRODUCTIONF
+      ("Done p6est_partition shipped %lld layers %.3g%%\n",
+       (long long) shipped,
+       shipped * 100. / p6est->global_first_layer[p6est->mpisize]);
+
+    return shipped;
+  }
+
+  new_layers = sc_array_new_size (sizeof (p2est_quadrant_t), my_count);
+
+  /* initialize views that we can use to bsearch */
+  sc_array_init_data (&new_gfl_bsearch, new_gfl, sizeof (p4est_gloidx_t),
+                      (size_t) mpisize);
+  sc_array_init_data (&old_gfl_bsearch, old_gfl, sizeof (p4est_gloidx_t),
+                      (size_t) mpisize);
+
+  /* create the mpi recv requests */
+  recv_requests = sc_array_new (sizeof (sc_MPI_Request));
+  /* create the list of procs from which to receive */
+  recv_procs = sc_array_new (sizeof (int));
+
+  /* create the receive buffer */
+  if (!data_size) {
+    recv = sc_array_new_view (new_layers, 0, my_count);
+  }
+  else {
+    recv =
+      sc_array_new_size (sizeof (p2est_quadrant_t) + data_size, my_count);
+  }
+
+  /* find the first proc that owns layers to send to me */
+  search = sc_array_bsearch (&old_gfl_bsearch, new_gfl + rank,
+                             gloidx_compare_overlap);
+  P4EST_ASSERT (search >= 0 && search < mpisize);
+  overlap = search;
+  P4EST_ASSERT (old_gfl[overlap] <= new_gfl[rank] &&
+                new_gfl[rank] < old_gfl[overlap + 1]);
+
+  offset = new_gfl[rank];
+  self_offset = my_count;
+  self_count = 0;
+  /* for every proc whose old range overlaps with this rank's new range */
+  while (overlap < mpisize &&
+         old_gfl[overlap] < new_gfl[rank + 1] &&
+         new_gfl[rank] < old_gfl[overlap + 1]) {
+    /* get the count that this proc will send this rank */
+    recv_count = SC_MIN (new_gfl[rank + 1], old_gfl[overlap + 1]) - offset;
+    local_offset = offset - new_gfl[rank];
+    if (overlap != rank) {
+      if (recv_count) {
+        /* post the receive */
+        req = (sc_MPI_Request *) sc_array_push (recv_requests);
+        ip = (int *) sc_array_push (recv_procs);
+        *ip = overlap;
+        mpiret = sc_MPI_Irecv (sc_array_index (recv, local_offset),
+                               (int) (recv_count * recv->elem_size),
+                               sc_MPI_BYTE, overlap, P6EST_COMM_PARTITION,
+                               p6est->mpicomm, req);
+        SC_CHECK_MPI (mpiret);
+      }
+    }
+    else {
+      self_offset = local_offset;
+      self_count = recv_count;
+    }
+
+    offset += recv_count;
+
+    overlap++;
+  }
+  P4EST_ASSERT (offset == (size_t) new_gfl[rank + 1]);
+  nrecv = (int) recv_requests->elem_count;
+
+  /* create the mpi send requests */
+  send_requests = sc_array_new (sizeof (sc_MPI_Request));
+
+  /* create the send buffer */
+  if (!data_size) {
+    send = sc_array_new_view (old_layers, 0, old_layers->elem_count);
+  }
+  else {
+    send = sc_array_new_size (sizeof (p2est_quadrant_t) + data_size,
+                              old_layers->elem_count);
+  }
+
+  /* find the first proc that owns layers to send to me */
+  search = sc_array_bsearch (&new_gfl_bsearch, old_gfl + rank,
+                             gloidx_compare_overlap);
+  P4EST_ASSERT (search >= 0 && search < mpisize);
+  overlap = search;
+  P4EST_ASSERT (new_gfl[overlap] <= old_gfl[rank] &&
+                old_gfl[rank] < new_gfl[overlap + 1]);
+
+  offset = old_gfl[rank];
+  /* for every proc whose new range overlaps with this rank's old range */
+  while (overlap < mpisize &&
+         new_gfl[overlap] < old_gfl[rank + 1] &&
+         old_gfl[rank] < new_gfl[overlap + 1]) {
+    /* get the count that this proc will send this rank */
+    send_count = SC_MIN (old_gfl[rank + 1], new_gfl[overlap + 1]) - offset;
+    local_offset = offset - old_gfl[rank];
+    if (send_count) {
+      if (overlap != rank) {
+        if (data_size) {
+          /* pack data for shipping */
+          for (zz = 0; zz < (size_t) send_count; zz++) {
+            layer =
+              p2est_quadrant_array_index (old_layers, zz + local_offset);
+            packedlayer =
+              (p2est_quadrant_t *) sc_array_index (send, zz + local_offset);
+            *packedlayer = *layer;
+            packedlayer->p.user_data = NULL;
+            if (data_size) {
+              layer_data = (void *) (packedlayer + 1);
+              memcpy (layer_data, layer->p.user_data, data_size);
+              p6est_layer_free_data (p6est, layer);
+            }
+          }
+        }
+        /* post the send */
+        req = (sc_MPI_Request *) sc_array_push (send_requests);
+        mpiret = sc_MPI_Isend (sc_array_index (send, local_offset),
+                               (int) (send_count * send->elem_size),
+                               sc_MPI_BYTE, overlap, P6EST_COMM_PARTITION,
+                               p6est->mpicomm, req);
+        SC_CHECK_MPI (mpiret);
+      }
+      else {
+        P4EST_ASSERT ((size_t) send_count == self_count);
+        /* copy locally */
+        memcpy (sc_array_index (new_layers, self_offset),
+                sc_array_index (old_layers, offset - old_gfl[rank]),
+                self_count * new_layers->elem_size);
+      }
+    }
+    offset += send_count;
+    overlap++;
+  }
+  P4EST_ASSERT (offset == (size_t) old_gfl[rank + 1]);
+  nsend = (int) send_requests->elem_count;
+
+  nleft = nrecv;
+  array_of_indices = P4EST_ALLOC (int, nrecv);
+  while (nleft > 0) {
+    /* finalize some receives */
+    mpiret =
+      sc_MPI_Waitsome (nrecv, (sc_MPI_Request *) (recv_requests->array),
+                       &outcount, array_of_indices, sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+    P4EST_ASSERT (outcount != sc_MPI_UNDEFINED);
+    P4EST_ASSERT (outcount >= 0);
+    if (!outcount) {
+      continue;
+    }
+    for (i = 0; i < outcount; i++) {
+      int                 j;
+      j = array_of_indices[i];
+      p = *((int *) sc_array_index_int (recv_procs, j));
+      P4EST_ASSERT (p >= 0 && p < mpisize);
+      P4EST_ASSERT (p != rank);
+      if (data_size) {
+        /* get the range in the array of this section */
+        local_offset = SC_MAX (0, old_gfl[p] - new_gfl[rank]);
+        local_last =
+          SC_MIN (new_gfl[rank + 1], old_gfl[p + 1]) - new_gfl[rank];
+        P4EST_ASSERT (local_last > local_offset);
+        for (zz = local_offset; zz < (size_t) local_last; zz++) {
+          /* unpack */
+          layer = p2est_quadrant_array_index (new_layers, zz);
+          packedlayer = (p2est_quadrant_t *) sc_array_index (recv, zz);
+          *layer = *packedlayer;
+          layer_data = (void *) (packedlayer + 1);
+          layer->p.user_data = sc_mempool_alloc (p6est->user_data_pool);
+          memcpy (layer->p.user_data, layer_data, data_size);
+        }
+      }
+    }
+    nleft -= outcount;
+  }
+  P4EST_ASSERT (!nleft);
+  P4EST_FREE (array_of_indices);
+
+  /* clean up recv */
+  sc_array_destroy (recv);
+  sc_array_destroy (recv_requests);
+  sc_array_destroy (recv_procs);
+
+  /* switch to the new layers */
+  p6est->layers = new_layers;
+  p6est->global_first_layer = new_gfl;
+  P4EST_FREE (old_gfl);
+
+  /* wait for sends to complete */
+  mpiret = sc_MPI_Waitall (nsend, (sc_MPI_Request *) (send_requests->array),
+                           sc_MPI_STATUSES_IGNORE);
+  SC_CHECK_MPI (mpiret);
+
+  /* clean up send and old_layers */
+  sc_array_destroy (send);
+  sc_array_destroy (send_requests);
+  sc_array_destroy (old_layers);
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Done p6est_partition shipped %lld layers %.3g%%\n",
+     (long long) shipped,
+     shipped * 100. / p6est->global_first_layer[p6est->mpisize]);
+
+  return shipped;
+}
+
+p4est_gloidx_t
+p6est_partition (p6est_t * p6est, p6est_weight_t weight_fn)
+{
+  return p6est_partition_ext (p6est, 0, weight_fn);
+}
+
+static int
+p6est_column_refine_thin_layer (p6est_t * p6est,
+                                p4est_topidx_t which_tree,
+                                p4est_quadrant_t * column)
+{
+  int                 max_diff = *((int *) p6est->user_pointer);
+  int8_t              horz_level = column->level;
+  size_t              first, last, zz;
+  sc_array_t         *layers = p6est->layers;
+
+  P6EST_COLUMN_GET_RANGE (column, &first, &last);
+
+  for (zz = first; zz < last; zz++) {
+    p2est_quadrant_t   *layer = p2est_quadrant_array_index (layers, zz);
+    int8_t              vert_level = layer->level;
+
+    if (vert_level - horz_level > max_diff) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+static int
+p6est_layer_refine_thick_layer (p6est_t * p6est,
+                                p4est_topidx_t which_tree,
+                                p4est_quadrant_t * column,
+                                p2est_quadrant_t * layer)
+{
+  int                 min_diff = *((int *) p6est->user_pointer);
+  int8_t              horz_level = column->level;
+  int8_t              vert_level = layer->level;
+
+  if (vert_level - horz_level < min_diff) {
+    return 1;
+  }
+
+  return 0;
+}
+
+void
+p6est_balance_ext (p6est_t * p6est, p8est_connect_type_t btype,
+                   int max_diff, int min_diff,
+                   p6est_init_t init_fn, p6est_replace_t replace_fn)
+{
+  p4est_connect_type_t hbtype;
+  p6est_refine_col_data_t refine_col;
+  void               *orig_user_pointer = p6est->user_pointer;
+  p6est_profile_t    *profile;
+  int                 any_change;
+  int                 niter;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into p6est_balance with %lld total layers"
+                            " in %lld total columns\n",
+                            (long long) p6est->global_first_layer[p6est->
+                                                                  mpisize],
+                            (long long) p6est->columns->global_num_quadrants);
+  p4est_log_indent_push ();
+
+  /* first refine columns whose layers are too thin */
+  if (max_diff >= min_diff) {
+    int                 print_max_diff = SC_MIN (max_diff, P4EST_QMAXLEVEL);
+
+    print_max_diff = SC_MAX (print_max_diff, -P4EST_QMAXLEVEL);
+
+    P4EST_GLOBAL_PRODUCTIONF
+      ("Enforcing maximum layer width:height ratio 2^%d:1\n", print_max_diff);
+    p6est->user_pointer = (void *) &max_diff;
+    p6est_refine_columns_ext (p6est, 1, -1, p6est_column_refine_thin_layer,
+                              init_fn, replace_fn);
+    p6est->user_pointer = orig_user_pointer;
+  }
+
+  /* next perform the horizontal balancing */
+  if (btype == P8EST_CONNECT_FACE) {
+    hbtype = P4EST_CONNECT_FACE;
+  }
+  else {
+    hbtype = P4EST_CONNECT_FULL;
+  }
+  refine_col.refine_col_fn = NULL;
+  refine_col.init_fn = init_fn;
+  refine_col.replace_fn = replace_fn;
+  refine_col.user_pointer = orig_user_pointer;
+  p6est->user_pointer = (void *) &refine_col;
+  P4EST_GLOBAL_VERBOSE ("Balancing p4est for columns\n");
+  p4est_balance_ext (p6est->columns, hbtype, NULL,
+                     p6est_replace_column_split);
+  p6est->user_pointer = orig_user_pointer;
+  p6est_compress_columns (p6est);
+  p6est_update_offsets (p6est);
+
+  /* next refine layers that are too thick */
+  if (max_diff >= min_diff) {
+    int                 print_min_diff = SC_MIN (min_diff, P4EST_QMAXLEVEL);
+
+    print_min_diff = SC_MAX (print_min_diff, -P4EST_QMAXLEVEL);
+
+    P4EST_GLOBAL_PRODUCTIONF
+      ("Enforcing minimum layer width:height ratio 2^%d:1\n", print_min_diff);
+    p6est->user_pointer = (void *) &min_diff;
+    p6est_refine_layers_ext (p6est, 1, -1, p6est_layer_refine_thick_layer,
+                             init_fn, replace_fn);
+    p6est->user_pointer = orig_user_pointer;
+  }
+
+  /* finally, the real work: balance neighboring layers */
+
+  /* initialize the lnodes profile and balance locally */
+  profile =
+    p6est_profile_new_local (p6est, NULL, P6EST_PROFILE_UNION, btype, 2);
+  niter = 0;
+  do {
+    P4EST_GLOBAL_VERBOSE ("p6est_balance layer balancing iteration begin\n");
+    any_change = 0;
+    p6est_profile_balance_local (profile);
+    any_change = p6est_profile_sync (profile);
+    P4EST_GLOBAL_VERBOSE ("p6est_balance layer balancing iteration end\n");
+    niter++;
+  } while (any_change);
+  P4EST_GLOBAL_STATISTICSF ("p6est layers balanced in %d iterations\n",
+                            niter);
+
+  p6est_refine_to_profile (p6est, profile, init_fn, replace_fn);
+
+  p6est_profile_destroy (profile);
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done p6est_balance with %lld total layers "
+                            "in %lld total columns\n",
+                            (long long) p6est->global_first_layer[p6est->
+                                                                  mpisize],
+                            (long long) p6est->columns->global_num_quadrants);
+}
+
+void
+p6est_balance (p6est_t * p6est, p8est_connect_type_t btype,
+               p6est_init_t init_fn)
+{
+  p6est_balance_ext (p6est, btype, 0, 1, init_fn, NULL);
+}
+
+unsigned
+p2est_quadrant_checksum (sc_array_t * quadrants,
+                         sc_array_t * checkarray, size_t first_quadrant)
+{
+  int                 own_check;
+  size_t              kz, qcount;
+  unsigned            crc;
+  uint32_t           *check;
+  p2est_quadrant_t   *q;
+
+  qcount = quadrants->elem_count;
+
+  P4EST_ASSERT (quadrants->elem_size == sizeof (p2est_quadrant_t));
+  P4EST_ASSERT (first_quadrant <= qcount);
+
+  if (checkarray == NULL) {
+    checkarray = sc_array_new (4);
+    own_check = 1;
+  }
+  else {
+    P4EST_ASSERT (checkarray->elem_size == 4);
+    own_check = 0;
+  }
+
+  sc_array_resize (checkarray, (qcount - first_quadrant) * 2);
+  for (kz = first_quadrant; kz < qcount; ++kz) {
+    q = p2est_quadrant_array_index (quadrants, kz);
+    check =
+      (uint32_t *) sc_array_index (checkarray, (kz - first_quadrant) * 2);
+    check[0] = htonl ((uint32_t) q->z);
+    check[1] = htonl ((uint32_t) q->level);
+  }
+  crc = sc_array_checksum (checkarray);
+
+  if (own_check) {
+    sc_array_destroy (checkarray);
+  }
+
+  return crc;
+}
+
+unsigned
+p6est_checksum (p6est_t * p6est)
+{
+  uLong               columncrc, locallayercrc, layercrc;
+  sc_array_t          checkarray;
+  size_t              scount, globalscount;
+
+  columncrc = p4est_checksum (p6est->columns);
+
+  sc_array_init (&checkarray, 4);
+  locallayercrc =
+    (uLong) p2est_quadrant_checksum (p6est->layers, &checkarray, 0);
+  scount = 4 * checkarray.elem_count;
+  P4EST_ASSERT (scount == p6est->layers->elem_count * 8);
+  sc_array_reset (&checkarray);
+
+  layercrc = p4est_comm_checksum (p6est->columns, locallayercrc, scount);
+  /* 4 bytes per field, two fields */
+  globalscount = p6est->global_first_layer[p6est->mpisize] * 8;
+
+  return adler32_combine (columncrc, layercrc, globalscount);
+}
diff --git a/example/p6est/p6est.h b/example/p6est/p6est.h
new file mode 100644
index 0000000..d079385
--- /dev/null
+++ b/example/p6est/p6est.h
@@ -0,0 +1,605 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2013 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p6est.h
+ *
+ * A hybrid 2D+1D AMR extension.
+ *
+ * \ingroup p6est
+ */
+
+/** \defgroup p6est p6est
+ *
+ * A hybrid 2D+1D AMR extension.
+ *
+ * To include this component of the p4est library, configure p4est with the
+ * --enable-p6est option given.  This module provides a specific kind of
+ * anisotropic adaptive mesh refinement for 3D meshes: it organizes the
+ * hexahedral cells into columns.  Each column has the footprint of a 2D p4est
+ * quadrant.  The cells within a column can be individually refined
+ * vertically, and a whole column of cells can be refined horizontally.  When
+ * the forest is partitioned, each column is assigned to a single MPI process,
+ * i.e., a column cannot be split between processes.  Most of the main 2D / 3D
+ * interface is available for p6est: refinement and coarsening, balance, ghost
+ * layers, and i/o.  Finite element nodes can also be created using
+ * p6est_lnodes_new(): this creates nodes in the same data structure as
+ * p8est_lnodes_new().
+ */
+
+#ifndef P6EST_H
+#define P6EST_H
+
+/* 2+1D refinement is based on the 2D p4est datatypes */
+#include <p4est.h>
+/* We need p8est_connect_type_t typedef from p8est_connectivity */
+#include <p8est_connectivity.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** This structure holds the 2D+1D inter-tree connectivity information.
+ * It is essentially a wrapper of the 2D p4est_connecitivity_t datatype, with
+ * some additional information about how the third dimension is embedded.
+ */
+typedef struct p6est_connectivity
+{
+  p4est_connectivity_t *conn4;  /**< the 2D connecitvity; owned; vertices
+                                  interpreted as the vertices of the bottom of
+                                  the sheet */
+  double             *top_vertices;     /**< if NULL, uniform vertical profile,
+                                           otherwise the vertices of the top of
+                                           the sheet: should be the same size
+                                           as \a conn4->tree_to_vertex; owned. */
+  double              height[3];        /**< if \a top_vertices == NULL, this gives the
+                                           offset from the bottom of the sheet to
+                                           the top */
+}
+p6est_connectivity_t;
+
+/** Create a p6est_connectivity_t from a p4est_connectivity_t.  All fields
+ * are copied, so all inputs can be safey destroyed.
+ *
+ * \param[in] conn4         the 2D connectivity
+ * \param[in] top_vertices  if NULL, then the sheet has a uniform vertical
+ *                          profile; otherwise, \a top_vertices gives teh
+ *                          vertices of the top of the sheet; should be the
+ *                          same size as \a conn4->tree_to_vertex
+ * \param[in] height        if \a top_vertices == NULL, then this gives the
+ *                          offset fro the bottom of the sheet to the top.
+ *
+ * \return the 2D+1D connectivity information.
+ */
+p6est_connectivity_t *p6est_connectivity_new (p4est_connectivity_t * conn4,
+                                              double *top_vertices,
+                                              double height[3]);
+
+/** Destroy a p6est_connectivity structure */
+void                p6est_connectivity_destroy (p6est_connectivity_t * conn);
+
+/** Get the vertices of the corners of a tree.
+ *
+ * \param[in]  conn         the 2D+1D connectivity structure
+ * \param[in]  which_tree   a tree in the forest
+ * \param[out] vertices     the coordinates of the corners of the tree
+ */
+void                p6est_tree_get_vertices (p6est_connectivity_t * conn,
+                                             p4est_topidx_t which_tree,
+                                             double vertices[24]);
+
+/** Transform a quadrant coordinate into the space spanned by tree vertices.
+ *
+ * \param [in] connectivity     Connectivity must provide the vertices.
+ * \param [in] treeid           Identify the tree that contains x, y.
+ * \param [in] x, y             Quadrant coordinates relative to treeid.
+ * \param [out] vxy             Transformed coordinates in vertex space.
+ */
+void                p6est_qcoord_to_vertex (p6est_connectivity_t *
+                                            connectivity,
+                                            p4est_topidx_t treeid,
+                                            p4est_qcoord_t x,
+                                            p4est_qcoord_t y,
+                                            p4est_qcoord_t z, double vxyz[3]);
+
+/** A 1D quadrant datatype: this is used to encode a "layer" of a column in
+ * the 2D+1D AMR scheme.
+ */
+typedef struct p2est_quadrant
+{
+  p4est_qcoord_t      z;                /**< vertical coordinate */
+  int8_t              level,            /**< level of refinement */
+                      pad8;             /**< padding */
+  int16_t             pad16;            /**< padding */
+  union p6est_quadrant_data
+  {
+    void               *user_data;      /**< never changed by p4est */
+    long                user_long;      /**< never changed by p4est */
+    int                 user_int;       /**< never changed by p4est */
+    p4est_topidx_t      which_tree;     /**< the tree containing the quadrant */
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      int                 owner_rank;
+    }
+    piggy1; /**< of ghost layer, store the tree and owner rank */
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_topidx_t      from_tree;
+    }
+    piggy2; /**< of transformed layers, store the original tree and the
+                 target tree */
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      local_num;
+    }
+    piggy3; /**< of ghost layers, store the tree and index in the owner's
+                 numbering */
+  }
+  p; /**< a union of additional data attached to a layer */
+}
+p2est_quadrant_t;
+
+/** The p6est forest datatype */
+typedef struct p6est
+{
+  sc_MPI_Comm         mpicomm;          /**< MPI communicator */
+  int                 mpisize,          /**< number of MPI processes */
+                      mpirank;          /**< this process's MPI rank */
+  size_t              data_size;        /**< size of per-quadrant p.user_data
+                     (see p2est_quadrant_t::p2est_quadrant_data::user_data) */
+  void               *user_pointer;     /**< convenience pointer for users,
+                                             never touched by p4est */
+  p6est_connectivity_t *connectivity;   /**< topology of sheet, not owned. */
+  p4est_t            *columns;  /**< 2D description of column layout
+                                     built from \a connectivity */
+  sc_array_t         *layers;   /**< single array that stores
+                                     p2est_quadrant_t layers within columns */
+  sc_mempool_t       *user_data_pool;   /**< memory allocator for user data */
+                                        /* WARNING: This is NULL if data size
+                                         *          equals zero.  */
+  sc_mempool_t       *layer_pool;       /**< memory allocator
+                                             for temporary layers */
+  p4est_gloidx_t     *global_first_layer; /**< first global quadrant index for
+                                               each process and 1 beyond */
+}
+p6est_t;
+
+/** Callback function prototype to initialize the layers's user data.
+ *
+ * \param[in] p6est        the forest
+ * \param[in] which_tree   the tree in the forest
+ * \param[in] column       the column in the tree in the forest
+ * \param[in] layer        the layer in the column in the tree in the
+ *                         forest, whose \a user_data is to be initialized
+ */
+typedef void        (*p6est_init_t) (p6est_t * p6est,
+                                     p4est_topidx_t which_tree,
+                                     p4est_quadrant_t * column,
+                                     p2est_quadrant_t * layer);
+
+/** Callback function prototype to transfer information from outgoing layers
+ * to incoming layers.
+ *
+ * This is used by extended routines when the layers of an existing, valid
+ * p6est are changed.  The callback allows the user to make changes to newly
+ * initialized layers before the layers that they replace are destroyed.
+ *
+ * \param [in] num_outcolumns  The number of columns that contain the outgoing
+ *                             layers: will be either 1 or 4.
+ * \param [in] num_outlayers   The number of outgoing layers: will be either 1
+ *                             (a single layer is being refined), 2 (two
+ *                             layers are being vertically coarsened), or 4
+ *                             (four layers are being horizontally coarsened).
+ * \param [in] outcolumns      The columns of the outgoing layers
+ * \param [in] outlayers       The outgoing layers: after the callback, the
+ *                             user_data, if \a p6est->data_size is nonzero,
+ *                             will be destroyed.
+ * \param [in] num_incolumns   The number of columns that contain the outgoing
+ *                             layers: will be either 1 or 4.
+ * \param [in] num_inlayers    The number of incoming layers: will be either 1
+ *                             (coarsening), 2 (vertical refinement), or 4
+ *                             (horizontal refinement)
+ * \param [in] incolumns       The columns of the incoming layers
+ * \param [in,out] inlayers    The incoming layers: prior to the callback,
+ *                             the user_data, if \a p6est->data_size is nonzero,
+ *                             is allocated, and the p6est_init_t callback,
+ *                             if it has been provided, will be called.
+ */
+typedef void        (*p6est_replace_t) (p6est_t * p6est,
+                                        p4est_topidx_t which_tree,
+                                        int num_outcolumns,
+                                        int num_outlayers,
+                                        p4est_quadrant_t * outcolumns[],
+                                        p2est_quadrant_t * outlayers[],
+                                        int num_incolumns,
+                                        int num_inlayers,
+                                        p4est_quadrant_t * incolumns[],
+                                        p2est_quadrant_t * inlayers[]);
+
+/** Callback function prototype to decide whether to horizontally refine a
+ * column, i.e., horizontally refine all of the layers in the column.
+ * \return nonzero if the layer shall be refined.
+ */
+typedef int         (*p6est_refine_column_t) (p6est_t * p6est,
+                                              p4est_topidx_t which_tree,
+                                              p4est_quadrant_t * column);
+
+/** Callback function prototype to decide whether to vertically refine a
+ * layer.
+ * \return nonzero if the layer shall be refined.
+ */
+typedef int         (*p6est_refine_layer_t) (p6est_t * p6est,
+                                             p4est_topidx_t which_tree,
+                                             p4est_quadrant_t * column,
+                                             p2est_quadrant_t * layer);
+
+/** Callback function prototype to decide for horizontal coarsening.
+ * \param [in] columns      Pointers to 4 sibling columns.
+ * \return nonzero if the columns shall be replaced with their parent.
+ */
+typedef int         (*p6est_coarsen_column_t) (p6est_t * p6est,
+                                               p4est_topidx_t which_tree,
+                                               p4est_quadrant_t * columns[]);
+
+/** Callback function prototype to decide for vertical coarsening.
+ * \param [in] layers      Pointers to 2 vertical sibling layers.
+ * \return nonzero if the layers shall be replaced with their parent.
+ */
+typedef int         (*p6est_coarsen_layer_t) (p6est_t * p6est,
+                                              p4est_topidx_t which_tree,
+                                              p4est_quadrant_t * column,
+                                              p2est_quadrant_t * layers[]);
+
+/** Callback function prototype to calculate weights for partitioning.
+ * \return a 32bit integer >= 0 as the quadrant weight.
+ * \note    Global sum of weights must fit into a 64bit integer.
+ */
+typedef int         (*p6est_weight_t) (p6est_t * p6est,
+                                       p4est_topidx_t which_tree,
+                                       p4est_quadrant_t * column,
+                                       p2est_quadrant_t * layer);
+
+extern void        *P2EST_DATA_UNINITIALIZED;
+
+/** set statically allocated quadrant to defined values */
+#define P2EST_QUADRANT_INIT(q) \
+  ((void) memset ((q), -1, sizeof (p2est_quadrant_t)))
+
+/** Create a new forest.
+ *
+ * The new forest consists of equi-partitioned root quadrants.
+ * When there are more processors than trees, some processors are empty.
+ *
+ * \param [in] mpicomm       A valid MPI communicator.
+ * \param [in] connectivity  This is the connectivity information that
+ *                           the forest is built with.  Note the p6est
+ *                           does not take ownership of the memory.
+ * \param [in] data_size     This is the size of data for each quadrant which
+ *                           can be zero.  Then user_data_pool is set to NULL.
+ * \param [in] init_fn       Callback function to initialize the user_data
+ *                           which is already allocated automatically.
+ * \param [in] user_pointer  Assign to the user_pointer member of the p6est
+ *                           before init_fn is called the first time.
+ *
+ * \return This returns a valid forest.
+ *
+ * \note The connectivity structure must not be destroyed
+ *       during the lifetime of this forest.
+ */
+p6est_t            *p6est_new (sc_MPI_Comm mpicomm,
+                               p6est_connectivity_t * connectivity,
+                               size_t data_size,
+                               p6est_init_t init_fn, void *user_pointer);
+
+/** Create a new forest from an already created p4est that represents
+ * columns.
+ *
+ * \param [in] p4est         A valid p4est.  A deep copy will be created, so
+ *                           this can be destroyed without affectin the new
+ *                           p6est object.
+ * \param [in] top_vertices  the same as in p6est_conectivity_new()
+ * \param [in] height        the same as in p6est_conectivity_new()
+ * \param [in] min_zlevel    the same as in p6est_new()
+ * \param [in] data_size     the same as in p6est_new()
+ * \param [in] init_fn       the same as in p6est_new()
+ * \param [in] user_pointer  the same as in p6est_new()
+ *
+ * \return This returns a valid forest.  The user must destroy the
+ * connectivity for the new p6est independently.
+ */
+p6est_t            *p6est_new_from_p4est (p4est_t * p4est,
+                                          double *top_vertices,
+                                          double height[3], int min_zlevel,
+                                          size_t data_size,
+                                          p6est_init_t init_fn,
+                                          void *user_pointer);
+
+/** Destroy a p6est.
+ *
+ * \note The connectivity structure is not destroyed with the p6est.
+ */
+void                p6est_destroy (p6est_t * p6est);
+
+/** Make a deep copy of a p6est.
+ * The connectivity is not duplicated.
+ * Copying of quadrant user data is optional.
+ * If old and new data sizes are 0, the user_data field is copied regardless.
+ *
+ * \param [in]  copy_data  If true, data are copied.
+ *                         If false, data_size is set to 0.
+ * \return  Returns a valid p6est that does not depend on the input.
+ */
+p6est_t            *p6est_copy (p6est_t * input, int copy_data);
+
+/** Reset user pointer and element data.
+ * When the data size is changed the quadrant data is freed and allocated.
+ * The initialization callback is invoked on each quadrant.
+ * Old user_data content is disregarded.
+ *
+ * \param [in] data_size     This is the size of data for each quadrant which
+ *                           can be zero.  Then user_data_pool is set to NULL.
+ * \param [in] init_fn       Callback function to initialize the user_data
+ *                           which is already allocated automatically.
+ * \param [in] user_pointer  Assign to the user_pointer member of the p6est
+ *                           before init_fn is called the first time.
+ */
+void                p6est_reset_data (p6est_t * p6est, size_t data_size,
+                                      p6est_init_t init_fn,
+                                      void *user_pointer);
+
+/** Refine the columns of a sheet.
+ *
+ * \param [in,out] p6est The forest is changed in place.
+ * \param [in] refine_recursive Boolean to decide on recursive refinement.
+ * \param [in] refine_fn Callback function that must return true if a column
+ *                       shall be refined into smaller columns.  If
+ *                       refine_recursive is true, refine_fn is called for
+ *                       every existing and newly created column.
+ *                       Otherwise, it is called for every existing column.
+ *                       It is possible that a refinement request made by the
+ *                       callback is ignored.  To catch this case, you can
+ *                       examine whether init_fn gets called, or use
+ *                       p6est_refine_columns_ext in p6est_extended.h and examine
+ *                       whether replace_fn gets called.
+ * \param [in] init_fn   Callback function to initialize the user_data of newly
+ *                       created layers within columns, which are already
+ *                       allocated.  This function pointer may be NULL.
+ */
+void                p6est_refine_columns (p6est_t * p6est,
+                                          int refine_recursive,
+                                          p6est_refine_column_t refine_fn,
+                                          p6est_init_t init_fn);
+
+/** Refine the layers within the columns of a sheet.
+ *
+ * \param [in,out] p6est The forest is changed in place.
+ * \param [in] refine_recursive Boolean to decide on recursive refinement.
+ * \param [in] refine_fn Callback function that must return true if a layer
+ *                       shall be refined into smaller layers.  If
+ *                       refine_recursive is true, refine_fn is called for
+ *                       every existing and newly created layer.
+ *                       Otherwise, it is called for every existing layer.
+ *                       It is possible that a refinement request made by the
+ *                       callback is ignored.  To catch this case, you can
+ *                       examine whether init_fn gets called, or use
+ *                       p6est_refine_layers_ext in p6est_extended.h and examine
+ *                       whether replace_fn gets called.
+ * \param [in] init_fn   Callback function to initialize the user_data of newly
+ *                       created layers, which are already allocated.  This
+ *                       function pointer may be NULL.
+ */
+void                p6est_refine_layers (p6est_t * p6est,
+                                         int refine_recursive,
+                                         p6est_refine_layer_t refine_fn,
+                                         p6est_init_t init_fn);
+
+/** Coarsen the columns of a sheet.
+ *
+ * \param [in,out] p6est  The forest is changed in place.
+ * \param [in] coarsen_recursive Boolean to decide on recursive coarsening.
+ * \param [in] coarsen_fn Callback function that returns true if a
+ *                        family of columns shall be coarsened
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ */
+void                p6est_coarsen_columns (p6est_t * p6est,
+                                           int coarsen_recursive,
+                                           p6est_coarsen_column_t coarsen_fn,
+                                           p6est_init_t init_fn);
+
+/** Coarsen the layers of a sheet.
+ *
+ * \param [in,out] p6est  The forest is changed in place.
+ * \param [in] coarsen_recursive Boolean to decide on recursive coarsening.
+ * \param [in] coarsen_fn Callback function that returns true if a
+ *                        family of layers shall be coarsened
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ */
+void                p6est_coarsen_layers (p6est_t * p6est,
+                                          int coarsen_recursive,
+                                          p6est_coarsen_layer_t coarsen_fn,
+                                          p6est_init_t init_fn);
+
+/** Balance a forest.
+ *
+ * \param [in] p6est     The p6est to be worked on.
+ * \param [in] btype     Balance type (face, corner or default, full).
+ * \param [in] init_fn   Callback function to initialize the user_data
+ *                       which is already allocated automatically.
+ */
+void                p6est_balance (p6est_t * p6est,
+                                   p8est_connect_type_t btype,
+                                   p6est_init_t init_fn);
+
+typedef enum
+{
+  P6EST_COMM_PARTITION = 1,
+  P6EST_COMM_GHOST,
+  P6EST_COMM_BALANCE
+}
+p6est_comm_tag_t;
+
+/** Equally partition the forest.
+ *
+ * The forest will be partitioned between processors where they each
+ * have an approximately equal number of quadrants.
+ *
+ * Note that \a p6est->layers and \a p6est->global_first_layers may change
+ * during this call.  Address pointers referencing these objects from before
+ * \a p6est_partition is called become invalid.
+ *
+ * \param [in,out] p6est      The forest that will be partitioned.
+ * \param [in]     weight_fn  A weighting function or NULL
+ *                            for uniform partitioning.
+ */
+p4est_gloidx_t      p6est_partition (p6est_t * p6est,
+                                     p6est_weight_t weight_fn);
+
+/** Compute the checksum for a forest.
+ * Based on quadrant arrays only. It is independent of partition and mpisize.
+ * \return  Returns the checksum on processor 0 only. 0 on other processors.
+ */
+unsigned            p6est_checksum (p6est_t * p6est);
+
+/** Save the complete connectivity/p6est data to disk.  This is a collective
+ *
+ * operation that all MPI processes need to call.  All processes write
+ * into the same file, so the filename given needs to be identical over
+ * all parallel invocations.
+ * \param [in] filename    Name of the file to write.
+ * \param [in] p6est       Valid forest structure.
+ * \param [in] save_data   If true, the element data is saved.
+ *                         Otherwise, a data size of 0 is saved.
+ * \note            Aborts on file errors.
+ */
+void                p6est_save (const char *filename, p6est_t * p6est,
+                                int save_data);
+
+/** Load the complete connectivity/p6est structure from disk.
+ *
+ * \param [in] filename         Name of the file to read.
+ * \param [in] mpicomm          A valid MPI communicator.
+ * \param [in] data_size        Size of data for each quadrant which can be
+ *                              zero.  Then user_data_pool is set to NULL.
+ *                              If data_size is zero, load_data is ignored.
+ * \param [in] load_data        If true, the element data is loaded.  This is
+ *                              only permitted if the saved data size matches.
+ *                              If false, the stored data size is ignored.
+ * \param [in] user_pointer     Assign to the user_pointer member of the p6est
+ *                              before init_fn is called the first time.
+ * \param [out] connectivity    Connectivity must be destroyed separately.
+ * \return          Returns a valid forest structure. A pointer to a valid
+ *                  connectivity structure is returned through the last
+ *                  argument.
+ * \note            Aborts on file errors or invalid file contents.
+ */
+p6est_t            *p6est_load (const char *filename, sc_MPI_Comm mpicomm,
+                                size_t data_size, int load_data,
+                                void *user_pointer,
+                                p6est_connectivity_t ** connectivity);
+
+/** Return a pointer to a quadrant array element indexed by a size_t. */
+/*@unused@*/
+static inline p2est_quadrant_t *
+p2est_quadrant_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p2est_quadrant_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p2est_quadrant_t *) (array->array + sizeof (p2est_quadrant_t) * it);
+}
+
+/** Call sc_array_push for a quadrant array. */
+/*@unused@*/
+static inline p2est_quadrant_t *
+p2est_quadrant_array_push (sc_array_t * array)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p2est_quadrant_t));
+
+  return (p2est_quadrant_t *) sc_array_push (array);
+}
+
+/** Call sc_mempool_alloc for a mempool creating quadrants. */
+/*@unused@*/
+static inline p2est_quadrant_t *
+p2est_quadrant_mempool_alloc (sc_mempool_t * mempool)
+{
+  P4EST_ASSERT (mempool->elem_size == sizeof (p2est_quadrant_t));
+
+  return (p2est_quadrant_t *) sc_mempool_alloc (mempool);
+}
+
+/** Call sc_list pop for a quadrant array. */
+/*@unused@*/
+static inline p2est_quadrant_t *
+p2est_quadrant_list_pop (sc_list_t * list)
+{
+  return (p2est_quadrant_t *) sc_list_pop (list);
+}
+
+#define P6EST_COLUMN_GET_RANGE(q,f,l)                \
+  do {                                               \
+    *(f) = (size_t) (q)->p.piggy3.local_num;         \
+    *(l) = *(f) + (size_t) (q)->p.piggy3.which_tree; \
+  } while (0);
+
+#define P6EST_COLUMN_SET_RANGE(q,f,l)                        \
+  do {                                                       \
+    (q)->p.piggy3.local_num = (p4est_locidx_t) (f);          \
+    (q)->p.piggy3.which_tree = (p4est_topidx_t) ((l) - (f)); \
+  } while (0);
+
+/*@unused@*/
+static inline void
+p6est_layer_init_data (p6est_t * p6est, p4est_topidx_t which_tree,
+                       p4est_quadrant_t * column,
+                       p2est_quadrant_t * layer, p6est_init_t init_fn)
+{
+  if (p6est->data_size > 0) {
+    layer->p.user_data = sc_mempool_alloc (p6est->user_data_pool);
+  }
+  else {
+    layer->p.user_data = NULL;
+  }
+  if (init_fn != NULL) {
+    init_fn (p6est, which_tree, column, layer);
+  }
+}
+
+/*@unused@*/
+static inline void
+p6est_layer_free_data (p6est_t * p6est, p2est_quadrant_t * layer)
+{
+  if (p6est->data_size > 0) {
+    sc_mempool_free (p6est->user_data_pool, layer->p.user_data);
+  }
+  layer->p.user_data = NULL;
+}
+
+void                p6est_compress_columns (p6est_t * p6est);
+void                p6est_update_offsets (p6est_t * p6est);
+
+SC_EXTERN_C_END;
+
+#endif /* !P6EST_H */
diff --git a/example/p6est/p6est_extended.h b/example/p6est/p6est_extended.h
new file mode 100644
index 0000000..57be005
--- /dev/null
+++ b/example/p6est/p6est_extended.h
@@ -0,0 +1,266 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2013 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/********************************************************************
+ *                          IMPORTANT NOTE                          *
+ *                                                                  *
+ * These interfaces are intended for those who like finer control.  *
+ * The API offers extended versions of some basic p6est functions.  *
+ * The API may change without notice.                               *
+ ********************************************************************/
+
+/** \file p6est_extended.h
+ *
+ * Interface routines with extended capabilities.
+ *
+ * \ingroup p6est
+ */
+
+#ifndef P6EST_EXTENDED_H
+#define P6EST_EXTENDED_H
+
+#include <p6est.h>
+
+/** Create a new forest.
+ * This is a more general form of p6est_new().
+ * See the documentation of p6est_new() for basic usage.
+ *
+ * \param [in] min_quadrants    Minimum initial quadrants per processor.
+ *                              Makes the refinement pattern mpisize-specific.
+ * \param [in] min_level        The forest is horizontally refined at least to
+ *                              this level.  May be negative or 0, then it has
+ *                              no effect.
+ * \param [in] min_zlevel       The forest is vertically refined at least to
+ *                              this level.  May be negative or 0, then it has
+ *                              no effect.
+ * \param [in] fill_uniform     If true, fill the forest with a uniform mesh
+ *                              instead of the coarsest possible one.
+ *                              The latter is partition-specific so that
+ *                              is usually not a good idea.
+ */
+p6est_t            *p6est_new_ext (sc_MPI_Comm mpicomm,
+                                   p6est_connectivity_t * connectivity,
+                                   p4est_locidx_t min_quadrants,
+                                   int min_level, int min_zlevel,
+                                   int fill_uniform, size_t data_size,
+                                   p6est_init_t init_fn, void *user_pointer);
+
+/** Save the complete connectivity/p6est data to disk.
+ *
+ * This is a collective operation that all MPI processes need to call.  All
+ * processes write into the same file, so the filename given needs to be
+ * identical over all parallel invocations.  See p6est_load_ext() for
+ * information on the autopartition parameter.
+ *
+ * \param [in] filename    Name of the file to write.
+ * \param [in] p6est       Valid forest structure.
+ * \param [in] save_data   If true, the element data is saved.
+ *                         Otherwise, a data size of 0 is saved.
+ * \param [in] save_partition   If false, save file as if 1 core was used.
+ *                              If true, save core count and partition.
+ *                         Advantage: Partition can be recovered on loading
+ *                              with same mpisize and autopartition false.
+ *                         Disadvantage: Makes the file depend on mpisize.
+ *                  Either way the file can be loaded with autopartition true.
+ * \note            Aborts on file errors.
+ */
+void                p6est_save_ext (const char *filename, p6est_t * p6est,
+                                    int save_data, int save_partition);
+
+/** Load the complete connectivity/p6est structure from disk.
+ *
+ * It is possible to load the file with a different number of processors
+ * than has been used to write it.  The partition will then be uniform.
+ *
+ * \param [in] filename         Name of the file to read.
+ * \param [in] mpicomm          A valid MPI communicator.
+ * \param [in] data_size        Size of data for each quadrant which can be
+ *                              zero.  Then user_data_pool is set to NULL.
+ *                              If data_size is zero, load_data is ignored.
+ * \param [in] load_data        If true, the element data is loaded.  This is
+ *                              only permitted if the saved data size matches.
+ *                              If false, the stored data size is ignored.
+ * \param [in] autopartition    Ignore saved partition and make it uniform.
+ * \param [in] broadcasthead    Have only rank 0 read headers and bcast them.
+ * \param [in] user_pointer     Assign to the user_pointer member of the p6est
+ *                              before init_fn is called the first time.
+ * \param [out] connectivity    Connectivity must be destroyed separately.
+ * \return          Returns a valid forest structure. A pointer to a valid
+ *                  connectivity structure is returned through the last
+ *                  argument.
+ * \note            Aborts on file errors or invalid file contents.
+ */
+p6est_t            *p6est_load_ext (const char *filename, sc_MPI_Comm mpicomm,
+                                    size_t data_size, int load_data,
+                                    int autopartition, int broadcasthead,
+                                    void *user_pointer,
+                                    p6est_connectivity_t ** connectivity);
+
+/** Horizontally refine a forest with a bounded refinement level and a replace option.
+ *
+ * \param [in,out] p6est The forest is changed in place.
+ * \param [in] refine_recursive Boolean to decide on recursive refinement.
+ * \param [in] maxlevel   Maximum allowed refinement level (inclusive).
+ *                        If this is negative the level is restricted only
+ *                        by the compile-time constant QMAXLEVEL in p4est.h.
+ * \param [in] refine_fn  Callback function that must return true if a quadrant
+ *                        shall be refined.  If refine_recursive is true,
+ *                        refine_fn is called for every existing and newly
+ *                        created quadrant.  Otherwise, it is called for every
+ *                        existing quadrant.  It is possible that a refinement
+ *                        request made by the callback is ignored.  To catch
+ *                        this case, you can examine whether init_fn or
+ *                        replace_fn gets called.
+ * \param [in] init_fn    Callback function to initialize the user_data for
+ *                        newly created quadrants, which is guaranteed to be
+ *                        allocated.  This function pointer may be NULL.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace; may be NULL.
+ */
+void                p6est_refine_columns_ext (p6est_t * p6est,
+                                              int refine_recursive,
+                                              int maxlevel,
+                                              p6est_refine_column_t refine_fn,
+                                              p6est_init_t init_fn,
+                                              p6est_replace_t replace_fn);
+
+/** Vertically refine a forest with a bounded refinement level and a replace option.
+ *
+ * \param [in,out] p6est The forest is changed in place.
+ * \param [in] refine_recursive Boolean to decide on recursive refinement.
+ * \param [in] maxlevel   Maximum allowed refinement level (inclusive).
+ *                        If this is negative the level is restricted only
+ *                        by the compile-time constant QMAXLEVEL in p4est.h.
+ * \param [in] refine_fn  Callback function that must return true if a quadrant
+ *                        shall be refined.  If refine_recursive is true,
+ *                        refine_fn is called for every existing and newly
+ *                        created quadrant.  Otherwise, it is called for every
+ *                        existing quadrant.  It is possible that a refinement
+ *                        request made by the callback is ignored.  To catch
+ *                        this case, you can examine whether init_fn or
+ *                        replace_fn gets called.
+ * \param [in] init_fn    Callback function to initialize the user_data for
+ *                        newly created quadrants, which is guaranteed to be
+ *                        allocated.  This function pointer may be NULL.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace; may be NULL.
+ */
+void                p6est_refine_layers_ext (p6est_t * p6est,
+                                             int refine_recursive,
+                                             int maxlevel,
+                                             p6est_refine_layer_t refine_fn,
+                                             p6est_init_t init_fn,
+                                             p6est_replace_t replace_fn);
+
+/** Horizontally coarsen a forest.
+ * \param [in,out] p6est The forest is changed in place.
+ * \param [in] coarsen_recursive Boolean to decide on recursive coarsening.
+ * \param [in] callback_orphans Boolean to enable calling coarsen_fn even on
+ *                        non-families.  In this case, the second quadrant
+ *                        pointer in the argument list of the callback is NULL,
+ *                        subsequent pointers are undefined, and the return
+ *                        value is ignored.  If coarsen_recursive is true, it
+ *                        is possible that a quadrant is called once or more as
+ *                        an orphan and eventually becomes part of a family.
+ * \param [in] coarsen_fn Callback function that returns true if a
+ *                        family of quadrants shall be coarsened.
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace.
+ */
+void                p6est_coarsen_columns_ext (p6est_t * p6est,
+                                               int coarsen_recursive,
+                                               int callback_orphans,
+                                               p6est_coarsen_column_t
+                                               coarsen_fn,
+                                               p6est_init_t init_fn,
+                                               p6est_replace_t replace_fn);
+
+/** Vertically coarsen a forest.
+ * \param [in,out] p6est The forest is changed in place.
+ * \param [in] coarsen_recursive Boolean to decide on recursive coarsening.
+ * \param [in] callback_orphans Boolean to enable calling coarsen_fn even on
+ *                        non-families.  In this case, the second quadrant
+ *                        pointer in the argument list of the callback is NULL,
+ *                        subsequent pointers are undefined, and the return
+ *                        value is ignored.  If coarsen_recursive is true, it
+ *                        is possible that a quadrant is called once or more as
+ *                        an orphan and eventually becomes part of a family.
+ * \param [in] coarsen_fn Callback function that returns true if a
+ *                        family of quadrants shall be coarsened.
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace.
+ */
+void                p6est_coarsen_layers_ext (p6est_t * p6est,
+                                              int coarsen_recursive,
+                                              int callback_orphans,
+                                              p6est_coarsen_layer_t
+                                              coarsen_fn,
+                                              p6est_init_t init_fn,
+                                              p6est_replace_t replace_fn);
+
+/** Repartition the forest.
+ *
+ * The forest is partitioned between processors such that each processor
+ * has an approximately equal number of quadrants (or weight).
+ *
+ * \param [in,out] p6est      The forest that will be partitioned.
+ * \param [in]     partition_for_coarsening     If true, the partition
+ *                            is modified to allow one level of coarsening.
+ * \param [in]     weight_fn  A weighting function or NULL
+ *                            for uniform partitioning.
+ * \return         The global number of shipped quadrants
+ */
+p4est_gloidx_t      p6est_partition_ext (p6est_t * p6est,
+                                         int partition_for_coarsening,
+                                         p6est_weight_t weight_fn);
+
+/** 2:1 balance the size differences of neighboring elements in a forest.
+ * \param [in,out] p6est  The p6est to be worked on.
+ * \param [in] btype      Balance type (face or corner/full).
+ *                        Corner balance is almost never required when
+ *                        discretizing a PDE; just causes smoother mesh grading.
+ * \param [in] max_diff   The maximum difference between the horizontal
+ *                        refinement level and the vertical refinement level
+ * \param [in] min_diff   The minimum difference between the horizontal
+ *                        refinement level and the vertical refinement level
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace.
+ */
+void                p6est_balance_ext (p6est_t * p6est,
+                                       p8est_connect_type_t btype,
+                                       int max_diff, int min_diff,
+                                       p6est_init_t init_fn,
+                                       p6est_replace_t replace_fn);
+
+#endif
diff --git a/example/p6est/p6est_ghost.c b/example/p6est/p6est_ghost.c
new file mode 100644
index 0000000..02a5854
--- /dev/null
+++ b/example/p6est/p6est_ghost.c
@@ -0,0 +1,625 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2014 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p6est_ghost.h>
+
+static int
+p2est_quadrant_compare_piggy (const void *A, const void *B)
+{
+  const p2est_quadrant_t *a = (const p2est_quadrant_t *) A;
+  const p2est_quadrant_t *b = (const p2est_quadrant_t *) B;
+  p4est_topidx_t      tdiff;
+
+  tdiff = a->p.piggy3.which_tree - b->p.piggy3.which_tree;
+  if (tdiff) {
+    return tdiff;
+  }
+
+  return a->p.piggy3.local_num - b->p.piggy3.local_num;
+}
+
+static              size_t
+ghost_tree_type (sc_array_t * array, size_t zindex, void *data)
+{
+  p2est_quadrant_t   *q;
+
+  P4EST_ASSERT (array->elem_size == sizeof (p2est_quadrant_t));
+
+  q = (p2est_quadrant_t *) sc_array_index (array, zindex);
+  return (size_t) q->p.which_tree;
+}
+
+#ifdef __cplusplus
+typedef
+  p4est_quadrant::p4est_quadrant_data
+  p4est_quadrant_data_t;
+#else
+typedef union p4est_quadrant_data p4est_quadrant_data_t;
+#endif
+
+static void
+p6est_ghost_send_front_layers (p6est_ghost_t * ghost,
+                               int nneighin,
+                               p6est_t * p6est,
+                               p4est_locidx_t * recv_off,
+                               p4est_locidx_t * recv_count)
+{
+  sc_array_t         *recv_requests, *recv_procs;
+  int                 i, j, *ip;
+  int                 mpisize = p6est->mpisize, mpiret, nneighout;
+  p4est_locidx_t      lfirst, llast, lj, midx, lcount, loffset;
+  sc_MPI_Request     *req;
+  p4est_ghost_t      *cghost = ghost->column_ghost;
+  sc_array_t         *glayers = &ghost->ghosts;
+  sc_array_t         *cmirrors = &cghost->mirrors;
+  p4est_locidx_t     *cmpfo = cghost->mirror_proc_front_offsets;
+  p4est_locidx_t     *cmpf = cghost->mirror_proc_fronts;
+  sc_array_t         *send, *send_requests;
+  p4est_locidx_t     *mpm = ghost->mirror_proc_mirrors;
+  p4est_locidx_t     *mpo = ghost->mirror_proc_offsets;
+  p4est_locidx_t     *mpf = ghost->mirror_proc_fronts;
+  p4est_locidx_t     *mpfo = ghost->mirror_proc_front_offsets;
+  p4est_locidx_t     *lmpfo;
+  p4est_quadrant_t   *col;
+  p4est_topidx_t      which_tree;
+  p4est_tree_t       *tree;
+  size_t              zfirst, zlast, zz, old_count;
+  p4est_locidx_t      nlmirror;
+  sc_array_t         *layers = p6est->layers;
+  p2est_quadrant_t   *layer, *mlayer;
+  sc_array_t         *lmirrors = &ghost->mirrors, *new_mirrors;
+  sc_array_t         *nmpfa, *nmpma;
+  p4est_topidx_t      num_trees = ghost->num_trees;
+
+  /* create the recv data */
+  recv_requests =
+    sc_array_new_size (sizeof (sc_MPI_Request), (size_t) nneighin);
+  recv_procs = sc_array_new_size (sizeof (int), (size_t) nneighin);
+
+  /* post the receives */
+  j = 0;
+  for (i = 0; i < mpisize; i++) {
+    lfirst = recv_off[i];
+    lcount = recv_count[i];
+    if (lcount) {
+      req = (sc_MPI_Request *) sc_array_index (recv_requests, j);
+      ip = (int *) sc_array_index (recv_procs, j++);
+      *ip = i;
+      mpiret = sc_MPI_Irecv (sc_array_index (glayers, (size_t) lfirst),
+                             (int) lcount * sizeof (p2est_quadrant_t),
+                             sc_MPI_BYTE, i, P6EST_COMM_GHOST, p6est->mpicomm,
+                             req);
+      SC_CHECK_MPI (mpiret);
+    }
+  }
+  P4EST_ASSERT (j == nneighin);
+
+  /* loop over the column mirror fronts to determine counts */
+  lmpfo = P4EST_ALLOC (p4est_locidx_t, mpisize + 1);
+  loffset = 0;
+
+  j = 0;
+  for (i = 0; i < mpisize; i++) {
+    lmpfo[i] = loffset;
+    lfirst = cmpfo[i];
+    llast = cmpfo[i + 1];
+    if (lfirst == llast) {
+      continue;
+    }
+    j++;
+    for (lj = lfirst; lj < llast; lj++) {
+      midx = cmpf[lj];
+      col = p4est_quadrant_array_index (cmirrors, midx);
+      which_tree = col->p.piggy3.which_tree;
+      midx = col->p.piggy3.local_num;
+      tree = p4est_tree_array_index (p6est->columns->trees, which_tree);
+      col = p4est_quadrant_array_index (&tree->quadrants,
+                                        midx - tree->quadrants_offset);
+      P6EST_COLUMN_GET_RANGE (col, &zfirst, &zlast);
+      P4EST_ASSERT (zlast > zfirst);
+      loffset += (zlast - zfirst);
+    }
+  }
+  lmpfo[mpisize] = nlmirror = loffset;
+  nneighout = j;
+
+  send = sc_array_new_size (sizeof (p2est_quadrant_t), (size_t) nlmirror);
+  send_requests = sc_array_new_size (sizeof (sc_MPI_Request), nneighout);
+
+  /* fill the send buffer */
+  j = 0;
+  loffset = 0;
+  for (i = 0; i < mpisize; i++) {
+    P4EST_ASSERT (lmpfo[i] == loffset);
+    lfirst = cmpfo[i];
+    llast = cmpfo[i + 1];
+    lcount = llast - lfirst;
+    if (!lcount) {
+      continue;
+    }
+    req = (sc_MPI_Request *) sc_array_index (send_requests, j);
+    j++;
+    for (lj = lfirst; lj < llast; lj++) {
+      midx = cmpf[lj];
+      col = p4est_quadrant_array_index (cmirrors, midx);
+      which_tree = col->p.piggy3.which_tree;
+      midx = col->p.piggy3.local_num;
+      tree = p4est_tree_array_index (p6est->columns->trees, which_tree);
+      col = p4est_quadrant_array_index (&tree->quadrants,
+                                        midx - tree->quadrants_offset);
+      P6EST_COLUMN_GET_RANGE (col, &zfirst, &zlast);
+      for (zz = zfirst; zz < zlast; zz++) {
+        layer = p2est_quadrant_array_index (layers, zz);
+        mlayer = p2est_quadrant_array_index (send, loffset++);
+        *mlayer = *layer;
+        mlayer->p.piggy3.which_tree = which_tree;
+        mlayer->p.piggy3.local_num = (p4est_locidx_t) zz;
+      }
+    }
+    lfirst = lmpfo[i];
+    llast = lmpfo[i + 1];
+    lcount = llast - lfirst;
+    P4EST_ASSERT (lcount);
+    mpiret = sc_MPI_Isend (sc_array_index (send, (size_t) lfirst),
+                           (int) lcount * sizeof (p2est_quadrant_t),
+                           sc_MPI_BYTE, i, P6EST_COMM_GHOST, p6est->mpicomm,
+                           req);
+    P4EST_ASSERT (lmpfo[i + 1] == loffset);
+  }
+
+  /* update all of the mirror structures */
+  new_mirrors = sc_array_new_size (lmirrors->elem_size, lmirrors->elem_count);
+  sc_array_copy (new_mirrors, lmirrors);
+  old_count = lmirrors->elem_count;
+  mlayer = (p2est_quadrant_t *) sc_array_push_count (new_mirrors, nlmirror);
+  if (send->elem_count) {
+    memcpy (mlayer, sc_array_index (send, 0),
+            send->elem_count * send->elem_size);
+  }
+  sc_array_sort (new_mirrors, p2est_quadrant_compare_piggy);
+  sc_array_uniq (new_mirrors, p2est_quadrant_compare_piggy);
+
+  P4EST_ASSERT (new_mirrors->elem_count >= old_count);
+  if (new_mirrors->elem_count > old_count) {
+    p4est_topidx_t      ti;
+    sc_array_t          split;
+
+    /* update mirror_tree_offsets */
+    sc_array_init (&split, sizeof (size_t));
+    sc_array_split (new_mirrors, &split, (size_t) num_trees, ghost_tree_type,
+                    NULL);
+    P4EST_ASSERT (split.elem_count == (size_t) num_trees + 1);
+    for (ti = 0; ti <= num_trees; ti++) {
+      size_t             *ppz;
+
+      ppz = (size_t *) sc_array_index (&split, (size_t) ti);
+      ghost->mirror_tree_offsets[ti] = *ppz;
+    }
+    sc_array_reset (&split);
+  }
+
+  /* update mirror_proc_fronts */
+  nmpfa = sc_array_new (sizeof (p4est_locidx_t));
+
+  if (mpo == mpfo) {
+    /* mpo and mpfo will now diverge, need to be separate arrays */
+    ghost->mirror_proc_offsets = mpo =
+      P4EST_ALLOC (p4est_locidx_t, mpisize + 1);
+    memcpy (mpo, mpfo, (mpisize + 1) * sizeof (p4est_locidx_t));
+  }
+
+  for (i = 0; i < mpisize; i++) {
+    p4est_locidx_t     *lp;
+    size_t              offset = nmpfa->elem_count;
+
+    lp = (p4est_locidx_t *) sc_array_push_count (nmpfa, lmpfo[i + 1] -
+                                                 lmpfo[i]);
+    mpfo[i] = (p4est_locidx_t) offset;
+
+    for (zz = lmpfo[i]; zz < (size_t) lmpfo[i + 1]; zz++) {
+      ssize_t             idx;
+      p2est_quadrant_t   *q1 = p2est_quadrant_array_index (send, zz);
+
+      idx = sc_array_bsearch (new_mirrors, q1, p2est_quadrant_compare_piggy);
+      P4EST_ASSERT (idx >= 0);
+      *(lp++) = (p4est_locidx_t) idx;
+    }
+  }
+  mpfo[mpisize] = nmpfa->elem_count;
+  if (ghost->mirror_proc_fronts != NULL) {
+    if (ghost->mirror_proc_fronts != ghost->mirror_proc_mirrors) {
+      P4EST_FREE (ghost->mirror_proc_fronts);
+    }
+  }
+  ghost->mirror_proc_fronts = mpf =
+    P4EST_ALLOC (p4est_locidx_t, nmpfa->elem_count);
+  memcpy (mpf, nmpfa->array, nmpfa->elem_size * nmpfa->elem_count);
+  sc_array_destroy (nmpfa);
+
+  if (ghost->mirror_proc_mirrors == NULL) {
+    P4EST_ASSERT (ghost->mirror_proc_offsets == NULL);
+    /* this is the first ghost layer construction: the fronts are all of the
+     * mirrors */
+    ghost->mirror_proc_mirrors = ghost->mirror_proc_fronts;
+    ghost->mirror_proc_offsets = ghost->mirror_proc_front_offsets;
+  }
+  else {
+    /* update mirror_proc_mirrors */
+
+    nmpma = sc_array_new (sizeof (p4est_locidx_t));
+    for (i = 0; i < mpisize; i++) {
+      size_t              frontsize = mpfo[i + 1] - mpfo[i];
+      size_t              offset = nmpma->elem_count;
+      p4est_locidx_t      old_offset = mpo[i];
+      p4est_locidx_t     *lp;
+
+      old_count = mpo[i + 1] - old_offset;
+
+      mpo[i] = offset;
+
+      lp = (p4est_locidx_t *) sc_array_push_count (nmpma, old_count +
+                                                   frontsize);
+      memcpy (lp, mpf + mpfo[i], sizeof (p4est_locidx_t) * frontsize);
+      lp += frontsize;
+
+      if (old_count) {
+        sc_array_t          pview;
+
+        for (zz = 0; zz < old_count; zz++) {
+          ssize_t             idx;
+          p2est_quadrant_t   *q1 = p2est_quadrant_array_index (lmirrors,
+                                                               mpm[zz +
+                                                                   old_offset]);
+          idx = sc_array_bsearch (new_mirrors, q1,
+                                  p2est_quadrant_compare_piggy);
+          P4EST_ASSERT (idx >= 0);
+          *(lp++) = (p4est_locidx_t) idx;
+        }
+        sc_array_init_view (&pview, nmpma, offset, old_count + frontsize);
+        sc_array_sort (&pview, p4est_locidx_compare);
+        sc_array_reset (&pview);
+      }
+    }
+    mpo[mpisize] = nmpma->elem_count;
+    if (ghost->mirror_proc_mirrors != NULL) {
+      P4EST_FREE (ghost->mirror_proc_mirrors);
+    }
+    ghost->mirror_proc_mirrors = mpm =
+      P4EST_ALLOC (p4est_locidx_t, nmpma->elem_count);
+    memcpy (mpm, nmpma->array, nmpma->elem_size * nmpma->elem_count);
+    sc_array_destroy (nmpma);
+  }
+
+  sc_array_resize (lmirrors, new_mirrors->elem_count);
+  sc_array_copy (lmirrors, new_mirrors);
+  sc_array_destroy (new_mirrors);
+  P4EST_FREE (lmpfo);
+
+  {
+    int                 outcount;
+    int                *array_of_indices = P4EST_ALLOC (int, nneighin);
+    int                 nleft = nneighin;
+
+    /* finish the receives */
+    while (nleft) {
+      mpiret =
+        sc_MPI_Waitsome (nneighin, (sc_MPI_Request *) recv_requests->array,
+                         &outcount, array_of_indices, sc_MPI_STATUSES_IGNORE);
+      SC_CHECK_MPI (mpiret);
+
+      if (recv_off != ghost->proc_offsets) {
+        for (i = 0; i < outcount; i++) {
+          sc_array_t          pview;
+
+          j = array_of_indices[i];
+          j = *((int *) sc_array_index_int (recv_procs, j));
+
+          sc_array_init_view (&pview, &ghost->ghosts, ghost->proc_offsets[j],
+                              ghost->proc_offsets[j + 1] -
+                              ghost->proc_offsets[j]);
+          sc_array_sort (&pview, p2est_quadrant_compare_piggy);
+          sc_array_reset (&pview);
+        }
+      }
+      nleft -= outcount;
+    }
+
+    P4EST_FREE (array_of_indices);
+
+    sc_array_destroy (recv_requests);
+    sc_array_destroy (recv_procs);
+  }
+
+  /* finish the sends */
+  mpiret = sc_MPI_Waitall (nneighout, (sc_MPI_Request *) send_requests->array,
+                           sc_MPI_STATUSES_IGNORE);
+  SC_CHECK_MPI (mpiret);
+
+  sc_array_destroy (send);
+  sc_array_destroy (send_requests);
+
+#ifdef P4EST_DEBUG
+  {
+    p4est_quadrant_data_t *cdata;
+    p4est_locidx_t      ngcol = cghost->ghosts.elem_count;
+
+    /* create an array of the data for each ghost column */
+    cdata = P4EST_ALLOC (p4est_quadrant_data_t, ngcol);
+
+    /* get the ghost column data using p4est_ghost_exchange_data */
+    P4EST_ASSERT (sizeof (void *) == sizeof (p4est_quadrant_data_t));
+    p4est_ghost_exchange_data (p6est->columns, cghost, cdata);
+
+    for (zz = 0; zz < (size_t) ngcol; zz++) {
+      p4est_quadrant_t    dummy;
+      size_t              first, last, zy;
+      p4est_locidx_t      gfirst, glast;
+
+      col = p4est_quadrant_array_index (&cghost->ghosts, zz);
+      dummy.p = cdata[zz];
+      P6EST_COLUMN_GET_RANGE (&dummy, &first, &last);
+
+      gfirst = *((p4est_locidx_t *) sc_array_index
+                 (ghost->column_layer_offsets, zz));
+      glast = *((p4est_locidx_t *) sc_array_index
+                (ghost->column_layer_offsets, zz + 1));
+
+      P4EST_ASSERT ((size_t) (glast - gfirst) == last - first);
+
+      for (zy = gfirst; zy < (size_t) glast; zy++) {
+        layer = p2est_quadrant_array_index (&ghost->ghosts, zy);
+        P4EST_ASSERT (col->p.piggy3.which_tree == layer->p.piggy3.which_tree);
+        P4EST_ASSERT ((size_t) layer->p.piggy3.local_num ==
+                      (zy - gfirst) + first);
+      }
+    }
+
+    P4EST_FREE (cdata);
+  }
+#endif
+}
+
+static void
+p6est_ghost_fill_offsets (p6est_t * p6est, p6est_ghost_t * ghost)
+{
+  p4est_quadrant_data_t *cdata;
+  p4est_locidx_t     *clo, offset, il, count, nglayer, thiscol;
+  p4est_topidx_t      it, num_trees = ghost->num_trees;
+  p4est_locidx_t     *proc_off, *tree_off, *cproc_off, *ctree_off;
+  p4est_t            *columns = p6est->columns;
+  p4est_ghost_t      *cghost = ghost->column_ghost;
+  size_t              first, last;
+  int                 i, mpisize = ghost->mpisize;
+  p4est_locidx_t      ngcol =
+    (p4est_locidx_t) ghost->column_ghost->ghosts.elem_count;
+
+  sc_array_resize (ghost->column_layer_offsets, (size_t) ngcol + 1);
+
+  clo = (p4est_locidx_t *) sc_array_index (ghost->column_layer_offsets, 0);
+
+  /* create an array of the data for each ghost column */
+  cdata = P4EST_ALLOC (p4est_quadrant_data_t, ngcol);
+
+  /* get the ghost column data using p4est_ghost_exchange_data */
+  P4EST_ASSERT (sizeof (void *) == sizeof (p4est_quadrant_data_t));
+  p4est_ghost_exchange_data (columns, cghost, cdata);
+
+  /* fill column layer offsets using each ghost column's count */
+  offset = 0;
+  for (il = 0; il < ngcol; il++) {
+    p4est_quadrant_t    dummy;
+
+    clo[il] = offset;
+    dummy.p = cdata[il];
+    P6EST_COLUMN_GET_RANGE (&dummy, &first, &last);
+    count = last - first;
+    offset += count;
+  }
+  clo[ngcol] = nglayer = offset;
+  P4EST_FREE (cdata);
+
+  /* create the tree and proc offsets */
+  tree_off = ghost->tree_offsets;
+  proc_off = ghost->proc_offsets;
+  ctree_off = cghost->tree_offsets;
+  cproc_off = cghost->proc_offsets;
+
+  /* fill tree offsets */
+  tree_off[0] = 0;
+  for (it = 1; it < num_trees; it++) {
+    if (ctree_off[it] == ctree_off[it - 1]) {
+      tree_off[it] = tree_off[it - 1];
+    }
+    else {
+      thiscol = ctree_off[it];
+      tree_off[it] = clo[thiscol];
+    }
+  }
+  tree_off[num_trees] = nglayer;
+
+  /* fill proc offsets */
+  proc_off[0] = 0;
+  for (i = 1; i <= mpisize; i++) {
+    if (cproc_off[i] == cproc_off[i - 1]) {
+      proc_off[i] = proc_off[i - 1];
+    }
+    else {
+      if (i < mpisize) {
+        thiscol = cproc_off[i];
+        proc_off[i] = clo[thiscol];
+      }
+      else {
+        proc_off[i] = nglayer;
+      }
+      P4EST_ASSERT (proc_off[i] > proc_off[i - 1]);
+    }
+  }
+
+  sc_array_resize (&ghost->ghosts, (size_t) nglayer);
+}
+
+p6est_ghost_t      *
+p6est_ghost_new (p6est_t * p6est, p4est_connect_type_t btype)
+{
+  p4est_t            *columns = p6est->columns;
+  p4est_ghost_t      *cghost;
+  p6est_ghost_t      *ghost = P4EST_ALLOC (p6est_ghost_t, 1);
+  p4est_topidx_t      num_trees;
+  p4est_locidx_t     *proc_off, *proc_count;
+  int                 nneigh;
+  int                 i, mpisize;
+
+  P4EST_GLOBAL_PRODUCTION ("Into p6est_ghost_new\n");
+  p4est_log_indent_push ();
+
+  /* create the column ghost layer */
+  ghost->column_ghost = cghost = p4est_ghost_new (columns, btype);
+  ghost->mpisize = mpisize = cghost->mpisize;
+  ghost->num_trees = num_trees = cghost->num_trees;
+  ghost->btype = btype;
+
+  /* create the column layer offsets */
+  ghost->column_layer_offsets = sc_array_new (sizeof (p4est_locidx_t));
+  ghost->tree_offsets = P4EST_ALLOC (p4est_locidx_t, num_trees + 1);
+  ghost->proc_offsets = P4EST_ALLOC (int, mpisize + 1);
+  ghost->mirror_proc_front_offsets = P4EST_ALLOC (int, mpisize + 1);
+  ghost->mirror_tree_offsets = P4EST_ALLOC (p4est_locidx_t, num_trees + 1);
+  ghost->mirror_proc_fronts = NULL;     /* these three are set in p6est_ghost_send_front_layers */
+  ghost->mirror_proc_offsets = NULL;
+  ghost->mirror_proc_mirrors = NULL;
+  sc_array_init (&ghost->ghosts, sizeof (p2est_quadrant_t));
+  sc_array_init (&ghost->mirrors, sizeof (p2est_quadrant_t));
+
+  p6est_ghost_fill_offsets (p6est, ghost);
+
+  proc_off = ghost->proc_offsets;
+
+  /* create the proc counts */
+  proc_count = P4EST_ALLOC (int, mpisize);
+
+  nneigh = 0;
+  for (i = 0; i < mpisize; i++) {
+    proc_count[i] = proc_off[i + 1] - proc_off[i];
+    if (proc_count[i]) {
+      nneigh++;
+    }
+  }
+
+  p6est_ghost_send_front_layers (ghost, nneigh, p6est, proc_off, proc_count);
+
+  P4EST_FREE (proc_count);
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done p6est_ghost_new\n");
+
+  return ghost;
+}
+
+void
+p6est_ghost_destroy (p6est_ghost_t * ghost)
+{
+  p4est_ghost_destroy (ghost->column_ghost);
+  sc_array_destroy (ghost->column_layer_offsets);
+  sc_array_reset (&ghost->ghosts);
+  P4EST_FREE (ghost->tree_offsets);
+  P4EST_FREE (ghost->proc_offsets);
+  sc_array_reset (&ghost->mirrors);
+  P4EST_FREE (ghost->mirror_tree_offsets);
+  if (ghost->mirror_proc_fronts != ghost->mirror_proc_mirrors) {
+    P4EST_ASSERT (ghost->mirror_proc_front_offsets !=
+                  ghost->mirror_proc_offsets);
+    P4EST_FREE (ghost->mirror_proc_fronts);
+    P4EST_FREE (ghost->mirror_proc_front_offsets);
+  }
+  P4EST_FREE (ghost->mirror_proc_mirrors);
+  P4EST_FREE (ghost->mirror_proc_offsets);
+  P4EST_FREE (ghost);
+}
+
+void
+p6est_ghost_expand (p6est_t * p6est, p6est_ghost_t * ghost)
+{
+  int                 i, mpisize = ghost->mpisize;
+  p4est_t            *columns = p6est->columns;
+  p4est_ghost_t      *cghost = ghost->column_ghost;
+  p4est_locidx_t     *old_proc_off, *proc_comm_off;
+  p4est_locidx_t     *proc_off, *proc_count;
+  int                 nneigh = 0;
+
+  P4EST_GLOBAL_PRODUCTION ("Into p6est_ghost_expand\n");
+  p4est_log_indent_push ();
+
+  /* copy the old proc offsets */
+  old_proc_off = P4EST_ALLOC (p4est_locidx_t, mpisize + 1);
+  proc_comm_off = P4EST_ALLOC (p4est_locidx_t, mpisize + 1);
+  memcpy (old_proc_off, ghost->proc_offsets, (mpisize + 1) * sizeof
+          (p4est_locidx_t));
+
+  /* expand the columns */
+  p4est_ghost_expand (columns, cghost);
+
+  /* update the offsets */
+  p6est_ghost_fill_offsets (p6est, ghost);
+
+  proc_off = ghost->proc_offsets;
+  proc_count = P4EST_ALLOC (p4est_locidx_t, mpisize);
+
+  /* The ghosts array reflects the old processor offsets.  Move every
+   * processor's existing ghosts to the beginning of its new offset */
+  nneigh = 0;
+  for (i = mpisize - 1; i >= 0; i--) {
+    int                 old_proc_count, new_proc_count;
+
+    old_proc_count = old_proc_off[i + 1] - old_proc_off[i];
+    new_proc_count = proc_off[i + 1] - proc_off[i];
+    P4EST_ASSERT (new_proc_count >= old_proc_count);
+
+    if (old_proc_count) {
+      P4EST_ASSERT (proc_off[i] >= old_proc_off[i]);
+      memmove (sc_array_index (&ghost->ghosts, (size_t) proc_off[i]),
+               sc_array_index (&ghost->ghosts, (size_t) old_proc_off[i]),
+               (old_proc_count) * sizeof (p2est_quadrant_t));
+    }
+
+    proc_count[i] = new_proc_count - old_proc_count;
+    /* we are going to send the new layers into the new array, after the old
+     * layers, so the communication offset is the processor's range in the new
+     * array, shifted by the old count */
+    proc_comm_off[i] = proc_off[i] + old_proc_count;
+
+    if (proc_count[i]) {
+      nneigh++;
+    }
+  }
+  P4EST_FREE (old_proc_off);
+
+  p6est_ghost_send_front_layers (ghost, nneigh, p6est, proc_comm_off,
+                                 proc_count);
+
+  P4EST_FREE (proc_comm_off);
+  P4EST_FREE (proc_count);
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done p6est_ghost_expand\n");
+
+}
diff --git a/example/p6est/p6est_ghost.h b/example/p6est/p6est_ghost.h
new file mode 100644
index 0000000..334a38b
--- /dev/null
+++ b/example/p6est/p6est_ghost.h
@@ -0,0 +1,198 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2014 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p6est_ghost.h
+ *
+ * passing columns of layers and data to neighboring processes
+ *
+ * \ingroup p6est
+ */
+
+#ifndef P6EST_GHOST_H
+#define P6EST_GHOST_H
+
+#include <p6est.h>
+#include <p4est_ghost.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** columns of layers that neighbor the local domain */
+typedef struct p6est_ghost
+{
+  int                 mpisize;
+  p4est_topidx_t      num_trees;
+  p4est_connect_type_t btype; /**< which neighboring columns are in the ghost layer */
+
+  p4est_ghost_t      *column_ghost; /**< describes the ghost columns */
+  sc_array_t         *column_layer_offsets; /**< array of p4est_locidx_t type:
+                                              the offset of each ghost columns
+                                              within the \a ghosts array of
+                                              column-layers */
+
+  /** An array of column-layers which make up the ghost layer around \a
+   * p6est.  Their piggy3 data member is filled with their owner's tree
+   * and local number (cumulative over trees).  Quadrants are ordered in \c
+   * p4est_quadrant_compare_piggy order.  These are quadrants inside the
+   * neighboring tree, i.e., \c p4est_quadrant_is_inside is true for the
+   * quadrant and the neighboring tree.
+   */
+  sc_array_t          ghosts; /**< array of p2est_quadrant_t type */
+  p4est_locidx_t     *tree_offsets;     /**< num_trees + 1 ghost indices */
+  p4est_locidx_t     *proc_offsets;     /**< mpisize + 1 ghost indices */
+
+  /** An array of local quadrants that touch the parallel boundary from the
+   * inside, i.e., that are ghosts in the perspective of at least one other
+   * processor.  The storage convention is the same as for \c ghosts above.
+   */
+  sc_array_t          mirrors; /**< array of p4est_quadrant_t type */
+  p4est_locidx_t     *mirror_tree_offsets;      /**< num_trees + 1 mirror indices */
+  p4est_locidx_t     *mirror_proc_mirrors;      /**< indices into mirrors grouped by
+                                                   outside processor rank and
+                                                   ascending within each rank */
+  p4est_locidx_t     *mirror_proc_offsets;      /**< mpisize + 1 indices into 
+                                                   mirror_proc_mirrors */
+
+  p4est_locidx_t     *mirror_proc_fronts;       /**< like mirror_proc_mirrors,
+                                                   but limited to the
+                                                   outermost octants.  This is
+                                                   NULL until
+                                                   p4est_ghost_expand is
+                                                   called */
+  p4est_locidx_t     *mirror_proc_front_offsets;        /**< NULL until
+                                                           p4est_ghost_expand is
+                                                           called */
+
+}
+p6est_ghost_t;
+
+/** Calculate the memory usage of the ghost layer.
+ * \param [in] ghost    Ghost layer structure.
+ * \return              Memory used in bytes.
+ */
+size_t              p6est_ghost_memory_used (p6est_ghost_t * ghost);
+
+/** Builds the ghost layer.
+ *
+ * This will gather the quadrants from each neighboring proc to build
+ * one layer of face and corner based ghost elements around the ones they own.
+ *
+ * \param [in] p4est            The forest for which the ghost layer will be
+ *                              generated.
+ * \param [in] btype            Which ghosts to include (across face, corner
+ *                              or default, full).
+ * \return                      A fully initialized ghost layer.
+ */
+p6est_ghost_t      *p6est_ghost_new (p6est_t * p4est,
+                                     p4est_connect_type_t btype);
+
+/** Expand the size of the ghost layer and mirrors by one additional layer of
+ * adjacency.
+ * \param [in] p6est            The forest from which the ghost layer was
+ *                              generated.
+ * \param [in,out] ghost        The ghost layer to be expanded.
+ */
+void                p6est_ghost_expand (p6est_t * p6est,
+                                        p6est_ghost_t * ghost);
+
+/** Frees all memory used for the ghost layer. */
+void                p6est_ghost_destroy (p6est_ghost_t * ghost);
+
+/** Conduct binary search for exact match on a range of the ghost layer.
+ * \param [in] ghost            The ghost layer.
+ * \param [in] which_proc       The owner of the searched quadrant.  Can be -1.
+ * \param [in] which_tree       The tree of the searched quadrant.  Can be -1.
+ * \param [in] q                Valid quadrant is searched in the ghost layer.
+ * \return                      Offset in the ghost layer, or -1 if not found.
+ */
+ssize_t             p6est_ghost_bsearch (p6est_ghost_t * ghost,
+                                         int which_proc,
+                                         p4est_topidx_t which_tree,
+                                         const p4est_quadrant_t * column,
+                                         const p2est_quadrant_t * layer);
+
+/** Conduct binary search for ancestor on range of the ghost layer.
+ * \param [in] ghost            The ghost layer.
+ * \param [in] which_proc       The owner of the searched quadrant.  Can be -1.
+ * \param [in] which_tree       The tree of the searched quadrant.  Can be -1.
+ * \param [in] q                Valid quadrant's ancestor is searched.
+ * \return                      Offset in the ghost layer, or -1 if not found.
+ */
+ssize_t             p6est_ghost_contains (p6est_ghost_t * ghost,
+                                          int which_proc,
+                                          p4est_topidx_t which_tree,
+                                          const p4est_quadrant_t * column,
+                                          const p2est_quadrant_t * layer);
+
+/** Checks if layer exists in the local forest or the ghost layer.
+ *
+ * For quadrants across tree corners it checks if the quadrant exists
+ * in any of the corner neighbors, thus it can execute multiple queries.
+ *
+ * \param [in]  p4est        The forest in which to search for \a q
+ * \param [in]  ghost        The ghost layer in which to search for \a q
+ * \param [in]  treeid       The tree to which \a q belongs (can be extended).
+ * \param [in]  column       The column that is being searched for.
+ * \param [in]  layer        The layer that is being searched for.
+ * \param [in,out] exists_arr Must exist and be of of elem_size = sizeof (int)
+ *                           for inter-tree corner cases.  Is resized by this
+ *                           function to one entry for each corner search
+ *                           and set to true/false depending on its existence
+ *                           in the local forest or ghost_layer.
+ * \param [in,out] rproc_arr If not NULL is filled with one rank per query.
+ * \param [in,out] rquad_arr If not NULL is filled with one quadrant per query.
+ *                           Its piggy3 member is defined as well.
+ *
+ * \return true if the quadrant exists in the local forest or in the
+ *                  ghost_layer, and false if doesn't exist in either.
+ */
+int                 p6est_layer_exists (p6est_t * p6est,
+                                        p6est_ghost_t * ghost,
+                                        p4est_topidx_t treeid,
+                                        const p4est_quadrant_t * column,
+                                        const p2est_quadrant_t * layer,
+                                        sc_array_t * exists_arr,
+                                        sc_array_t * rproc_arr,
+                                        sc_array_t * rquad_arr);
+
+/** Check a forest to see if it is balanced.
+ *
+ * This function builds the ghost layer and discards it when done.
+ *
+ * \param [in] p4est    The p4est to be tested.
+ * \param [in] btype    Balance type (face, corner or default, full).
+ * \return Returns true if balanced, false otherwise.
+ */
+int                 p6est_is_balanced (p6est_t * p6est,
+                                       p8est_connect_type_t btype);
+
+/** Compute the parallel checksum of a ghost layer.
+ * \param [in] p4est   The MPI information of this p4est will be used.
+ * \param [in] ghost   A ghost layer obtained from the p4est.
+ * \return             Parallel checksum on rank 0, 0 otherwise.
+ */
+unsigned            p6est_ghost_checksum (p6est_t * p6est,
+                                          p6est_ghost_t * ghost);
+
+SC_EXTERN_C_END;
+
+#endif /* P6EST_GHOST_H */
diff --git a/example/p6est/p6est_io.c b/example/p6est/p6est_io.c
new file mode 100644
index 0000000..e69de29
diff --git a/example/p6est/p6est_io.h b/example/p6est/p6est_io.h
new file mode 100644
index 0000000..e69de29
diff --git a/example/p6est/p6est_lnodes.c b/example/p6est/p6est_lnodes.c
new file mode 100644
index 0000000..966b25e
--- /dev/null
+++ b/example/p6est/p6est_lnodes.c
@@ -0,0 +1,399 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2014 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p6est_lnodes.h>
+#include <p6est_profile.h>
+
+p6est_lnodes_t     *
+p6est_lnodes_new (p6est_t * p6est, p6est_ghost_t * ghost, int degree)
+{
+  p6est_lnodes_t     *lnodes;
+  p6est_profile_t    *profile;
+  p4est_lnodes_t     *clnodes;
+  int                 nperelem = (degree + 1) * (degree + 1) * (degree + 1);
+  /* int nperface = (degree - 1) * (degree - 1); */
+  /* int nperedge = (degree - 1); */
+  p4est_locidx_t      ncid, cid, enid, *en;
+  p4est_locidx_t      nnodecols;
+  p4est_locidx_t      nelemcols;
+  p4est_locidx_t      nll;
+  p4est_locidx_t      nlayers;
+  p4est_locidx_t     *layernodecount;
+  p4est_locidx_t     *layernodeoffsets;
+  p4est_locidx_t (*lr)[2];
+  p4est_locidx_t      ncolnodes;
+  p4est_locidx_t     *global_owned_count;
+  p4est_locidx_t      num_owned, num_local;
+  p4est_gloidx_t      gnum_owned, offset;
+  p4est_gloidx_t     *owned_offsets;
+  int                 i, j, k;
+  int                 mpisize = p6est->mpisize;
+  int                 mpiret;
+  sc_array_t          lnoview;
+  size_t              zz, nsharers;
+  int                 Nrp = degree + 1;
+
+  if (degree == 1) {
+    p4est_locidx_t      eid, nid, enid2, nid2;
+    p4est_locidx_t     *newnum, newlocal, newowned;
+
+    P4EST_GLOBAL_PRODUCTION ("Into adapt p6est_lnodes_new for degree = 1\n");
+    p4est_log_indent_push ();
+    /* adapt 2 to 1 */
+
+    lnodes = p6est_lnodes_new (p6est, ghost, 2);
+    nll = p6est->layers->elem_count;
+    num_local = lnodes->num_local_nodes;
+    num_owned = lnodes->owned_count;
+
+    en = lnodes->element_nodes;
+
+    newnum = P4EST_ALLOC (p4est_locidx_t, P8EST_INSUL * nll);
+    memset (newnum, -1, P8EST_INSUL * nll * sizeof (p4est_locidx_t));
+
+    for (enid = 0, eid = 0; eid < nll; eid++) {
+      for (k = 0; k < 3; k++) {
+        for (j = 0; j < 3; j++) {
+          for (i = 0; i < 3; i++, enid++) {
+            if (k != 1 && j != 1 && i != 1) {
+              newnum[en[enid]] = 0;
+            }
+          }
+        }
+      }
+    }
+
+    newlocal = 0;
+    newowned = 0;
+    for (nid = 0; nid < num_local; nid++) {
+      if (newnum[nid] >= 0) {
+        newnum[nid] = newlocal++;
+        if (nid < num_owned) {
+          newowned++;
+        }
+      }
+    }
+
+    /* compress en */
+    enid2 = 0;
+    for (enid = 0, eid = 0; eid < nll; eid++) {
+      for (k = 0; k < 3; k++) {
+        for (j = 0; j < 3; j++) {
+          for (i = 0; i < 3; i++, enid++) {
+            if (k != 1 && j != 1 && i != 1) {
+              en[enid2++] = newnum[en[enid]];
+            }
+          }
+        }
+      }
+    }
+    P4EST_ASSERT (enid2 == P8EST_CHILDREN * nll);
+    lnodes->element_nodes =
+      P4EST_REALLOC (en, p4est_locidx_t, P8EST_CHILDREN * nll);
+
+    owned_offsets = P4EST_ALLOC (p4est_gloidx_t, mpisize + 1);
+
+    mpiret = sc_MPI_Allgather (&newowned, 1, P4EST_MPI_LOCIDX,
+                               lnodes->global_owned_count, 1,
+                               P4EST_MPI_LOCIDX, p6est->mpicomm);
+
+    owned_offsets[0] = 0;
+    for (i = 0; i < mpisize; i++) {
+      owned_offsets[i + 1] = owned_offsets[i] + lnodes->global_owned_count[i];
+    }
+    lnodes->global_offset = owned_offsets[p6est->mpirank];
+    lnodes->num_local_nodes = newlocal;
+    lnodes->owned_count = newowned;
+    lnodes->degree = 1;
+    lnodes->vnodes = P8EST_CHILDREN;
+
+    lnodes->nonlocal_nodes =
+      P4EST_REALLOC (lnodes->nonlocal_nodes, p4est_gloidx_t,
+                     newlocal - newowned);
+
+    nsharers = lnodes->sharers->elem_count;
+    for (zz = 0; zz < nsharers; zz++) {
+      size_t              nshared, zy, zw;
+      p6est_lnodes_rank_t *rank = p6est_lnodes_rank_array_index
+        (lnodes->sharers, zz);
+
+      if (rank->owned_count) {
+        if (rank->rank != p6est->mpirank) {
+          p4est_locidx_t      newrankowned = 0;
+          p4est_locidx_t      newrankoffset = -1;
+
+          for (nid = rank->owned_offset; nid < rank->owned_offset +
+               rank->owned_count; nid++) {
+            if (newnum[nid] >= 0) {
+              lnodes->nonlocal_nodes[newnum[nid] - newowned] =
+                owned_offsets[rank->rank];
+              newrankowned++;
+              if (newrankoffset < 0) {
+                newrankoffset = newnum[nid];
+              }
+            }
+          }
+          rank->owned_offset = newrankoffset;
+          rank->owned_count = newrankowned;
+        }
+        else {
+          rank->owned_offset = 0;
+          rank->owned_count = newowned;
+        }
+      }
+      rank->shared_mine_count = 0;
+      rank->shared_mine_offset = -1;
+      zw = 0;
+      nshared = rank->shared_nodes.elem_count;
+      for (zy = 0; zy < nshared; zy++) {
+
+        nid = *((p4est_locidx_t *) sc_array_index (&rank->shared_nodes, zy));
+        if (newnum[nid] >= 0) {
+          p4est_locidx_t     *lp;
+
+          lp = (p4est_locidx_t *) sc_array_index (&rank->shared_nodes, zw++);
+          *lp = newnum[nid];
+          if (newnum[nid] < newowned) {
+            rank->shared_mine_count++;
+            if (rank->shared_mine_offset == -1) {
+              rank->shared_mine_offset = zw - 1;
+            }
+          }
+        }
+      }
+      sc_array_resize (&rank->shared_nodes, zw);
+    }
+
+    /* send local numbers to others */
+    {
+      sc_array_t          view;
+
+      sc_array_init_data (&view, newnum, sizeof (p4est_locidx_t), newlocal);
+
+      p6est_lnodes_share_owned (&view, lnodes);
+    }
+
+    nid2 = 0;
+    for (nid = num_owned; nid < num_local; nid++) {
+      if (newnum[nid] >= 0) {
+        lnodes->nonlocal_nodes[nid2++] += (p4est_gloidx_t) newnum[nid];
+      }
+    }
+    P4EST_ASSERT (nid2 == newlocal - newowned);
+
+    P4EST_FREE (owned_offsets);
+    P4EST_FREE (newnum);
+
+    p4est_log_indent_pop ();
+    P4EST_GLOBAL_PRODUCTION ("Done adapt p6est_lnodes_new for degree = 1\n");
+
+    return lnodes;
+  }
+
+  P4EST_GLOBAL_PRODUCTION ("Into p6est_lnodes_new\n");
+  p4est_log_indent_push ();
+
+  P4EST_ASSERT (degree >= 1);
+
+  lnodes = P4EST_ALLOC (p6est_lnodes_t, 1);
+
+  /* first get the profile */
+  profile = p6est_profile_new_local (p6est, ghost, P6EST_PROFILE_INTERSECTION,
+                                     P8EST_CONNECT_FULL, degree);
+  p6est_profile_sync (profile);
+
+  lr = (p4est_locidx_t (*)[2]) profile->lnode_ranges;
+
+  clnodes = profile->lnodes;
+
+  nnodecols = clnodes->num_local_nodes;
+  nelemcols = clnodes->num_local_elements;
+  en = clnodes->element_nodes;
+  layernodecount = P4EST_ALLOC_ZERO (p4est_locidx_t, nnodecols);
+  layernodeoffsets = P4EST_ALLOC_ZERO (p4est_locidx_t, nnodecols + 1);
+  for (cid = 0, enid = 0; cid < nelemcols; cid++) {
+    for (j = 0; j < Nrp; j++) {
+      for (i = 0; i < Nrp; i++, enid++) {
+        ncid = en[enid];
+        nlayers = lr[ncid][1];
+        P4EST_ASSERT (nlayers);
+        ncolnodes = nlayers * degree + 1;
+        layernodecount[ncid] = ncolnodes;
+      }
+    }
+  }
+
+  num_owned = 0;
+  num_local = 0;
+  for (ncid = 0; ncid < nnodecols; ncid++) {
+    num_local += layernodecount[ncid];
+    if (ncid < clnodes->owned_count) {
+      num_owned += layernodecount[ncid];
+    }
+  }
+
+  P4EST_VERBOSEF ("p6est_lnodes: %d owned %d local\n", num_owned, num_local);
+
+  if (nnodecols) {
+    layernodeoffsets[0] = 0;
+    for (ncid = 0; ncid < nnodecols; ncid++) {
+      layernodeoffsets[ncid + 1] = layernodeoffsets[ncid] +
+        layernodecount[ncid];
+    }
+  }
+
+  gnum_owned = num_owned;
+
+  owned_offsets = P4EST_ALLOC (p4est_gloidx_t, mpisize + 1);
+  global_owned_count = P4EST_ALLOC (p4est_locidx_t, mpisize);
+
+  mpiret = sc_MPI_Allgather (&gnum_owned, 1, P4EST_MPI_GLOIDX,
+                             owned_offsets, 1, P4EST_MPI_GLOIDX,
+                             p6est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  offset = 0;
+  for (i = 0; i < mpisize; i++) {
+    global_owned_count[i] = (p4est_locidx_t) owned_offsets[i];
+    gnum_owned = owned_offsets[i];
+    owned_offsets[i] = offset;
+    offset += gnum_owned;
+  }
+  owned_offsets[mpisize] = offset;
+
+  nll = p6est->layers->elem_count;
+  nsharers = clnodes->sharers->elem_count;
+
+  lnodes->mpicomm = p6est->mpicomm;
+  lnodes->num_local_nodes = num_local;
+  lnodes->owned_count = num_owned;
+  lnodes->global_offset = owned_offsets[p6est->mpirank];
+  lnodes->nonlocal_nodes =
+    P4EST_ALLOC (p4est_gloidx_t, num_local - num_owned);
+  lnodes->sharers =
+    sc_array_new_size (sizeof (p6est_lnodes_rank_t), nsharers);
+  lnodes->global_owned_count = global_owned_count;
+
+  lnodes->degree = degree;
+  lnodes->vnodes = nperelem;
+  lnodes->num_local_elements = nll;
+  lnodes->face_code = P4EST_ALLOC (p6est_lnodes_code_t, nll);
+  lnodes->element_nodes = P4EST_ALLOC (p4est_locidx_t, nperelem * nll);
+
+  p6est_profile_element_to_node (p6est, profile, layernodeoffsets,
+                                 lnodes->element_nodes, lnodes->face_code);
+
+  for (zz = 0; zz < nsharers; zz++) {
+    p4est_lnodes_rank_t *crank = p4est_lnodes_rank_array_index
+      (clnodes->sharers, zz);
+    p6est_lnodes_rank_t *rank = p6est_lnodes_rank_array_index
+      (lnodes->sharers, zz);
+    size_t              zy;
+    size_t              nshared;
+
+    rank->rank = crank->rank;
+    sc_array_init (&rank->shared_nodes, sizeof (p4est_locidx_t));
+    nshared = crank->shared_nodes.elem_count;
+
+    rank->owned_offset = -1;
+    rank->owned_count = 0;
+    rank->shared_mine_count = 0;
+    rank->shared_mine_offset = -1;
+    for (zy = 0; zy < nshared; zy++) {
+      p4est_locidx_t      cnid =
+        *((p4est_locidx_t *) sc_array_index (&crank->shared_nodes, zy));
+      p4est_locidx_t     *lp;
+      p4est_locidx_t      nthis, il;
+      p4est_locidx_t      old_count = rank->shared_nodes.elem_count;
+
+      nthis = layernodecount[cnid];
+      lp =
+        (p4est_locidx_t *) sc_array_push_count (&rank->shared_nodes, nthis);
+
+      for (il = 0; il < nthis; il++) {
+        lp[il] = layernodeoffsets[cnid] + il;
+        if (zy >= (size_t) crank->shared_mine_offset
+            && (p4est_locidx_t) zy - crank->shared_mine_offset <
+            crank->shared_mine_count) {
+          rank->shared_mine_count++;
+          if (rank->shared_mine_offset == -1) {
+            rank->shared_mine_offset = old_count + il;
+          }
+        }
+        if (cnid >= crank->owned_offset
+            && cnid - crank->owned_offset < crank->owned_count) {
+          rank->owned_count++;
+          if (rank->owned_offset == -1) {
+            rank->owned_offset = lp[il];
+          }
+        }
+      }
+    }
+    if (rank->rank == p6est->mpirank) {
+      rank->owned_offset = 0;
+      rank->owned_count = num_owned;
+    }
+  }
+
+  memcpy (layernodecount, layernodeoffsets,
+          nnodecols * sizeof (p4est_locidx_t));
+  sc_array_init_data (&lnoview, layernodecount, sizeof (p4est_locidx_t),
+                      (size_t) nnodecols);
+
+  p4est_lnodes_share_owned (&lnoview, clnodes);
+
+  for (zz = 0; zz < nsharers; zz++) {
+    p4est_lnodes_rank_t *crank = p4est_lnodes_rank_array_index
+      (clnodes->sharers, zz);
+
+    if (crank->rank == p6est->mpirank) {
+      continue;
+    }
+
+    for (ncid = crank->owned_offset;
+         ncid < crank->owned_offset + crank->owned_count; ncid++) {
+      p4est_gloidx_t      owners_offset;
+      p4est_locidx_t      nid;
+
+      P4EST_ASSERT (ncid >= clnodes->owned_count);
+      owners_offset = owned_offsets[crank->rank] + layernodecount[ncid];
+      for (nid = layernodeoffsets[ncid]; nid < layernodeoffsets[ncid + 1];
+           nid++) {
+        P4EST_ASSERT (nid >= num_owned);
+        P4EST_ASSERT (nid < num_local);
+        lnodes->nonlocal_nodes[nid - num_owned] = owners_offset++;
+      }
+    }
+  }
+
+  p6est_profile_destroy (profile);
+
+  P4EST_FREE (owned_offsets);
+  P4EST_FREE (layernodecount);
+  P4EST_FREE (layernodeoffsets);
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done p6est_lnodes_new\n");
+
+  return lnodes;
+}
diff --git a/example/p6est/p6est_lnodes.h b/example/p6est/p6est_lnodes.h
new file mode 100644
index 0000000..cb2e7e7
--- /dev/null
+++ b/example/p6est/p6est_lnodes.h
@@ -0,0 +1,271 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2014 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P6EST_LNODES_H
+#define P6EST_LNODES_H
+
+#include <p6est.h>
+#include <p6est_ghost.h>
+#include <p4est_lnodes.h>
+#include <p8est_lnodes.h>
+
+SC_EXTERN_C_BEGIN;
+
+/* A p6est_lnodes_t is exactly the same as a p8est_lnodes_t, with the only
+ * difference being that the face_codes are interpreted differently to account
+ * for the types of hanging faces that occur in a p6est.  Please see the
+ * documentation for p8est_lnodes */
+
+/* The only other differece is in the numbering of nodes and the number of
+ * faces.
+ *
+ * Columns of nodes are numbered contiguously: this still generates a
+ * partition-unique numbering.
+ *
+ * Although we call a p2est_quadrant_t coordinate layer->z, the orientaton of
+ * a layer from lnodes perspective is that the vertical axis is the X axis of
+ * the 3D element, the x axis of the columns is the Y axis of the 3D element,
+ * and the y axis of the columns is the Z axis of the 3D element
+ */
+
+typedef p8est_lnodes_t p6est_lnodes_t;
+typedef p8est_lnodes_code_t p6est_lnodes_code_t;
+typedef p8est_lnodes_rank_t p6est_lnodes_rank_t;
+typedef p8est_lnodes_buffer_t p6est_lnodes_buffer_t;
+
+/** Decode the face_code into hanging face information.
+ *
+ * \param[in] face_code as in the p6est_lnodes_t structure.
+ * \param[out] hanging_face: if there are hanging faces or edges,
+ *             hanging_face = -1 if the face is not hanging,
+ *                          = the corner of the full face that it touches:
+ *                            e.g. if face = i and hanging_face[i] =
+ *                            j, then the interpolation operator corresponding
+ *                            to corner j should be used for that face.
+ *             note: not touched if there are no hanging faces or edges.
+ * \param[out] hanging_edge: if there are hanging faces or edges,
+ *             hanging_edge = -1 if the edge is not hanging,
+ *                          =  0 if the edge is the first half of a full edge,
+ *                               but neither of the two faces touching the
+ *                               edge is hanging,
+ *                          =  1 if the edge is the second half of a full edge,
+ *                               but neither of the two faces touching the
+ *                               edge is hanging,
+ *                          =  2 if the edge is the first half of a full edge
+ *                               and is on the boundary of a full face,
+ *                          =  3 if the edge is the second half of a full edge
+ *                               and is on the boundary of a full face,
+ *                          =  4 if the edge is in the middle of a full face.
+ *                               See the diagram below for clarification.
+ *             note: not touched if there are no hanging faces or edges.
+ * \return             true if any face or edge is hanging, false otherwise.
+ *
+ * o...............o  o...............o  +---2---+.......o  o.......+---3---+
+ * :               :  :               :  |       |       :  :       |       |
+ * :               :  :               :  3   2   4       :  :       4   3   3
+ * :               :  :               :  |       |       :  :       |       |
+ * +---4---+       :  :       +---4---+  +---4---+       :  :       +---4---+
+ * |       |       :  :       |       |  :               :  :               :
+ * 2   0   4       :  :       4   1   2  :               :  :               :
+ * |       |       :  :       |       |  :               :  :               :
+ * +---2---+.......o  o.......+---3---+  o...............o  o...............o
+ *
+ * o...............o  +-----(-1)------+  +---2---+.......o  o.......+---3---+
+ * :               :  |               |  |       |       :  :       |       |
+ * :               :  3       5       3  |       |       :  :       |       |
+ * :               :  |               |  |       |       :  :       |       |
+ * +-------4-------+  +-------4-------+ -1   6   4       :  :       4   7  -1
+ * |               |  :               :  |       |       :  :       |       |
+ * 2       4       2  :               :  |       |       :  :       |       |
+ * |               |  :               :  |       |       :  :       |       |
+ * +-----(-1)------+  o...............o  +---2---+.......o  o.......+---3---+
+ *
+ *                    o                  +-------+
+ *                    :                  |\       \
+ *                    :                  1 \       \
+ *                    :                  |  +-------+
+ *                    +-------+          +  |       |
+ *                    |\       \         :\ |       |
+ *                    0 \       \        : \|       |
+ *                    |  +-------+       :  +-------+
+ *                    +  |       |       o
+ *                     \ |       |
+ *                      \|       |
+ *                       +-------+
+ */
+/*@unused@*/
+static inline int
+p6est_lnodes_decode (p6est_lnodes_code_t face_code, int hanging_face[6],
+                     int hanging_edge[12])
+{
+  P4EST_ASSERT (face_code >= 0);
+
+  if (face_code) {
+    /* we pack the p4est_lnodes_code_t at the bottom, followed by a bit
+     * indicating whether this layer is a first or second sibling, followed by
+     * four bits indicating which of the four side faces are layerwise
+     * nonconforming, followed by four bits indicating which of the four side
+     * edges are layerwise nonconforming */
+    p4est_lnodes_code_t fc4 = face_code & 0x000f;
+    int16_t             h = (face_code & 0x0010) >> 4;
+    int16_t             work = face_code >> 5;
+    int                 hf;
+    int                 f, e, w;
+
+    memset (hanging_face, -1, 6 * sizeof (int));
+    memset (hanging_edge, -1, 12 * sizeof (int));
+
+    /* the first two faces are the top and bottom faces, which we know are not
+     * hanging */
+    p4est_lnodes_decode (fc4, hanging_face + 2);
+    for (f = 0; f < 4; f++) {
+      hf = hanging_face[f + 2];
+      w = work & 0x0001;
+      if (hf >= 0) {
+        hanging_edge[p8est_face_edges[f + 2][2]] = 2 + hf;
+        hanging_edge[p8est_face_edges[f + 2][3]] = 2 + hf;
+        hanging_edge[p8est_face_edges[f + 2][1 ^ hf]] = 4;
+        if (w) {
+          hanging_edge[p8est_face_edges[f + 2][3 ^ h]] = 4;
+          hanging_edge[p8est_face_edges[f + 2][1 ^ hf]] = 4;
+          hanging_edge[p8est_face_edges[f + 2][hf]] = 2 + h;
+          hanging_face[f + 2] = (hf << 1) | h;
+        }
+        else {
+          hanging_face[f + 2] = 4 + hf;
+        }
+      }
+      else if (w) {
+        hanging_edge[p8est_face_edges[f + 2][3 ^ h]] = 4;
+        hanging_edge[p8est_face_edges[f + 2][0]] =
+          SC_MAX (hanging_edge[p8est_face_edges[f + 2][0]], 2 + h);
+        hanging_edge[p8est_face_edges[f + 2][1]] =
+          SC_MAX (hanging_edge[p8est_face_edges[f + 2][1]], 2 + h);
+        hanging_face[f + 2] = 6 + h;
+      }
+      work >>= 1;
+    }
+    for (e = 0; e < 4; e++) {
+      if (work & 0x0001) {
+        if (hanging_edge[e] < 0) {
+          hanging_edge[e] = h;
+        }
+#ifdef P4EST_DEBUG
+        else {
+          P4EST_ASSERT (hanging_edge[e] == 2 + h || hanging_edge[e] == 4);
+        }
+#endif
+      }
+      work >>= 1;
+    }
+    return 1;
+  }
+  else {
+    return 0;
+  }
+}
+
+p6est_lnodes_t     *p6est_lnodes_new (p6est_t * p6est,
+                                      p6est_ghost_t * ghost_layer,
+                                      int degree);
+
+static inline void
+p6est_lnodes_destroy (p6est_lnodes_t * lnodes)
+{
+  p8est_lnodes_destroy (lnodes);
+}
+
+/*@unused@*/
+static inline p6est_lnodes_buffer_t *
+p6est_lnodes_share_owned_begin (sc_array_t * node_data,
+                                p6est_lnodes_t * lnodes)
+{
+  return p8est_lnodes_share_owned_begin (node_data, lnodes);
+}
+
+/*@unused@*/
+static inline void
+p6est_lnodes_share_owned_end (p6est_lnodes_buffer_t * buffer)
+{
+  p8est_lnodes_share_owned_end (buffer);
+}
+
+/*@unused@*/
+static inline void
+p6est_lnodes_share_owned (sc_array_t * node_data, p6est_lnodes_t * lnodes)
+{
+  p8est_lnodes_share_owned (node_data, lnodes);
+}
+
+/*@unused@*/
+static inline p6est_lnodes_buffer_t *
+p6est_lnodes_share_all_begin (sc_array_t * node_data, p6est_lnodes_t * lnodes)
+{
+  return p8est_lnodes_share_all_begin (node_data, lnodes);
+}
+
+/*@unused@*/
+static inline void
+p6est_lnodes_share_all_end (p6est_lnodes_buffer_t * buffer)
+{
+  p8est_lnodes_share_all_end (buffer);
+}
+
+/*@unused@*/
+static inline p6est_lnodes_buffer_t *
+p6est_lnodes_share_all (sc_array_t * node_data, p6est_lnodes_t * lnodes)
+{
+  return p8est_lnodes_share_all (node_data, lnodes);
+}
+
+/*@unused@*/
+static inline void
+p6est_lnodes_buffer_destroy (p6est_lnodes_buffer_t * buffer)
+{
+  p8est_lnodes_buffer_destroy (buffer);
+}
+
+/*@unused@*/
+static inline p6est_lnodes_rank_t *
+p6est_lnodes_rank_array_index_int (sc_array_t * array, int it)
+{
+  return p8est_lnodes_rank_array_index_int (array, it);
+}
+
+/*@unused@*/
+static inline p6est_lnodes_rank_t *
+p6est_lnodes_rank_array_index (sc_array_t * array, size_t it)
+{
+  return p8est_lnodes_rank_array_index (array, it);
+}
+
+/*@unused@*/
+static inline       p4est_gloidx_t
+p6est_lnodes_global_index (p6est_lnodes_t * lnodes, p4est_locidx_t lidx)
+{
+  return p8est_lnodes_global_index (lnodes, lidx);
+}
+
+SC_EXTERN_C_END;
+
+#endif /* !P6EST_LNODES */
diff --git a/example/p6est/p6est_profile.c b/example/p6est/p6est_profile.c
new file mode 100644
index 0000000..dbcea7d
--- /dev/null
+++ b/example/p6est/p6est_profile.c
@@ -0,0 +1,1246 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2014 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p6est_profile.h>
+#include <p4est_bits.h>
+
+/* given two profiles (layers that have been reduced to just their levels),
+ * take the union, i.e. combine them, taking the finer layers */
+static void
+p6est_profile_union (sc_array_t * a, sc_array_t * b, sc_array_t * c)
+{
+  size_t              az, bz, na;
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (c));
+  P4EST_ASSERT (a->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (b->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (c->elem_size == sizeof (int8_t));
+  int8_t              al, bl, finel, *cc;
+  p4est_qcoord_t      finesize, coarsesize;
+  sc_array_t         *finer;
+  size_t             *fineincr;
+
+  sc_array_truncate (c);
+  az = 0;
+  bz = 0;
+  na = a->elem_count;
+  while (az < na) {
+    P4EST_ASSERT (bz < b->elem_count);
+
+    cc = (int8_t *) sc_array_push (c);
+
+    al = *((int8_t *) sc_array_index (a, az++));
+    bl = *((int8_t *) sc_array_index (b, bz++));
+    if (al == bl) {
+      *cc = al;
+      continue;
+    }
+    else if (al > bl) {
+      finer = a;
+      finesize = P4EST_QUADRANT_LEN (al);
+      fineincr = &az;
+      finel = al;
+      coarsesize = P4EST_QUADRANT_LEN (bl);
+    }
+    else {
+      finer = b;
+      finesize = P4EST_QUADRANT_LEN (bl);
+      fineincr = &bz;
+      finel = bl;
+      coarsesize = P4EST_QUADRANT_LEN (al);
+    }
+
+    P4EST_ASSERT (finesize < coarsesize);
+
+    do {
+      *cc = finel;
+      cc = (int8_t *) sc_array_push (c);
+      finel = *((int8_t *) sc_array_index (finer, (*fineincr)++));
+      finesize += P4EST_QUADRANT_LEN (finel);
+    } while (finesize < coarsesize);
+    P4EST_ASSERT (finesize == coarsesize);
+    *cc = finel;
+  }
+}
+
+/* given two profiles (layers that have been reduced to just their levels),
+ * take the intersection, i.e. combine them, taking the coarser layers */
+static void
+p6est_profile_intersection (sc_array_t * a, sc_array_t * b, sc_array_t * c)
+{
+  size_t              az, bz, na;
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (c));
+  P4EST_ASSERT (a->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (b->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (c->elem_size == sizeof (int8_t));
+  int8_t              al, bl, finel, *cc;
+  p4est_qcoord_t      finesize, coarsesize;
+  sc_array_t         *finer;
+  size_t             *fineincr;
+
+  sc_array_truncate (c);
+  az = 0;
+  bz = 0;
+  na = a->elem_count;
+  while (az < na) {
+    P4EST_ASSERT (bz < b->elem_count);
+
+    cc = (int8_t *) sc_array_push (c);
+
+    al = *((int8_t *) sc_array_index (a, az++));
+    bl = *((int8_t *) sc_array_index (b, bz++));
+    if (al == bl) {
+      *cc = al;
+      continue;
+    }
+    else if (al > bl) {
+      *cc = bl;
+      finer = a;
+      finesize = P4EST_QUADRANT_LEN (al);
+      fineincr = &az;
+      finel = al;
+      coarsesize = P4EST_QUADRANT_LEN (bl);
+    }
+    else {
+      *cc = al;
+      finer = b;
+      finesize = P4EST_QUADRANT_LEN (bl);
+      fineincr = &bz;
+      finel = bl;
+      coarsesize = P4EST_QUADRANT_LEN (al);
+    }
+
+    P4EST_ASSERT (finesize < coarsesize);
+
+    do {
+      finel = *((int8_t *) sc_array_index (finer, (*fineincr)++));
+      finesize += P4EST_QUADRANT_LEN (finel);
+    } while (finesize < coarsesize);
+    P4EST_ASSERT (finesize == coarsesize);
+  }
+}
+
+static void
+p6est_profile_balance_self_one_pass (sc_array_t * read, sc_array_t * write)
+{
+  int                 stackcount;
+  int8_t              n, newn, p, l;
+  int8_t             *wc;
+  size_t              count = read->elem_count;
+  size_t              zy;
+
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (write));
+  P4EST_ASSERT (read->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (write->elem_size == sizeof (int8_t));
+
+  sc_array_truncate (write);
+  wc = (int8_t *) sc_array_push (write);
+  n = *((int8_t *) sc_array_index (read, count - 1));
+  *wc = l = n;
+  for (zy = 1; zy < count; zy++) {
+    n = *((int8_t *) sc_array_index (read, count - 1 - zy));
+    p = l - 1;
+    newn = SC_MAX (p, n);
+    stackcount = newn - n;
+    wc = (int8_t *) sc_array_push_count (write, 1 + stackcount);
+    *wc = l = newn;
+    while (stackcount--) {
+      *(++wc) = l = newn--;
+    }
+  }
+}
+
+static void
+p6est_profile_balance_self (sc_array_t * a, sc_array_t * work)
+{
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (a));
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (work));
+  P4EST_ASSERT (a->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (work->elem_size == sizeof (int8_t));
+
+  p6est_profile_balance_self_one_pass (a, work);
+  p6est_profile_balance_self_one_pass (work, a);
+}
+
+static void
+p6est_profile_balance_face_one_pass (sc_array_t * read, sc_array_t * write)
+{
+  int8_t             *wc;
+  size_t              count;
+  int                 stackcount;
+  int8_t              n, nn, newn, p, l;
+  p4est_qcoord_t      readh;
+  size_t              zy;
+
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (write));
+  P4EST_ASSERT (read->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (write->elem_size == sizeof (int8_t));
+
+  count = read->elem_count;
+
+  sc_array_truncate (write);
+  l = 0;
+  zy = 0;
+  readh = 0;
+  while (zy < count) {
+    n = *((int8_t *) sc_array_index (read, count - 1 - zy++));
+    if (n && !(readh & P4EST_QUADRANT_LEN (n))) {
+      P4EST_ASSERT (zy < count);
+      nn = *((int8_t *) sc_array_index (read, count - 1 - zy));
+      if (n == nn) {
+        zy++;
+        n--;
+      }
+    }
+    readh += P4EST_QUADRANT_LEN (n);
+    p = l - 1;
+    newn = SC_MAX (p, n);
+    stackcount = newn - n;
+    wc = (int8_t *) sc_array_push_count (write, 1 + stackcount);
+    *wc = l = newn;
+    while (stackcount--) {
+      *(++wc) = l = newn--;
+    }
+  }
+}
+
+/* assumes a is already self balanced */
+static void
+p6est_profile_balance_face (sc_array_t * a, sc_array_t * b, sc_array_t * work)
+{
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (b));
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (work));
+  P4EST_ASSERT (a->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (b->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (work->elem_size == sizeof (int8_t));
+
+  p6est_profile_balance_face_one_pass (a, work);
+  p6est_profile_balance_self_one_pass (work, b);
+}
+
+static void
+p6est_profile_balance_full_one_pass (sc_array_t * read, sc_array_t * write)
+{
+  int8_t             *wc;
+  size_t              count;
+  int                 stackcount;
+  int8_t              n, nn, newn, p, l, prevl, nextl;
+  p4est_qcoord_t      readh;
+  size_t              zy;
+
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (write));
+  P4EST_ASSERT (read->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (write->elem_size == sizeof (int8_t));
+
+  count = read->elem_count;
+
+  sc_array_truncate (write);
+  l = 0;
+  zy = 0;
+  readh = 0;
+  while (zy < count) {
+    n = *((int8_t *) sc_array_index (read, count - 1 - zy++));
+    if (n && !(readh & P4EST_QUADRANT_LEN (n))) {
+      P4EST_ASSERT (zy < count);
+      nn = *((int8_t *) sc_array_index (read, count - 1 - zy));
+      if (n == nn) {
+        if (zy > 1) {
+          prevl = *((int8_t *) sc_array_index (read, count - 1 - (zy - 2)));
+        }
+        else {
+          prevl = -1;
+        }
+        if (zy < count - 1) {
+          nextl = *((int8_t *) sc_array_index (read, count - 1 - (zy + 1)));
+        }
+        else {
+          nextl = -1;
+        }
+        if (n >= SC_MAX (nextl, prevl) - 1) {
+          zy++;
+          n--;
+        }
+      }
+    }
+    readh += P4EST_QUADRANT_LEN (n);
+    p = l - 1;
+    newn = SC_MAX (p, n);
+    stackcount = newn - n;
+    wc = (int8_t *) sc_array_push_count (write, 1 + stackcount);
+    *wc = l = newn;
+    while (stackcount--) {
+      *(++wc) = l = newn--;
+    }
+  }
+}
+
+/* assumes a is already self balanced */
+static void
+p6est_profile_balance_full (sc_array_t * a, sc_array_t * b, sc_array_t * work)
+{
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (b));
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (work));
+  P4EST_ASSERT (a->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (b->elem_size == sizeof (int8_t));
+  P4EST_ASSERT (work->elem_size == sizeof (int8_t));
+
+  p6est_profile_balance_full_one_pass (a, work);
+  p6est_profile_balance_self_one_pass (work, b);
+}
+
+static void
+p6est_profile_element_to_node_single (sc_array_t * elem, sc_array_t * node,
+                                      int degree, p4est_locidx_t offset,
+                                      p4est_locidx_t ** elem_to_node,
+                                      p6est_lnodes_code_t * fc, int fcoffset)
+{
+  size_t              nedge = node->elem_count;
+  size_t              az, bz;
+  int                 i;
+
+  P4EST_ASSERT (degree > 1);
+
+  az = 0;
+
+  for (bz = 0; bz < nedge; bz++) {
+    int8_t              a;
+    int8_t              b = *((int8_t *) sc_array_index (node, bz));
+    int                 loop = 0;
+
+    do {
+      a = *((int8_t *) sc_array_index (elem, az));
+      P4EST_ASSERT (a == b || a == b + 1);
+      loop = !loop && (a == b + 1);
+      for (i = 0; i < degree + 1; i++) {
+        elem_to_node[az][i] = offset + bz * degree + i;
+      }
+      if (fc && a == b + 1) {
+        fc[az] |= (1 << (fcoffset + 5));
+      }
+      az++;
+    } while (loop);
+  }
+  P4EST_ASSERT (az == elem->elem_count);
+}
+
+static void
+p6est_profile_element_to_node_col (p6est_profile_t * profile,
+                                   p4est_locidx_t cid,
+                                   p4est_locidx_t * offsets,
+                                   p4est_locidx_t * e_to_n,
+                                   p6est_lnodes_code_t * fc)
+{
+  p4est_locidx_t (*lr)[2] = (p4est_locidx_t (*)[2]) profile->lnode_ranges;
+  p4est_locidx_t      nelem;
+  p4est_locidx_t    **elem_to_node;
+  int                 i, j, k;
+  p4est_locidx_t      ll;
+  sc_array_t          elem, node;
+  sc_array_t         *lc = profile->lnode_columns;
+  p4est_locidx_t      ncid, nid;
+  p4est_lnodes_code_t fc4 = profile->lnodes->face_code[cid];
+  p4est_locidx_t     *en = profile->lnodes->element_nodes;
+  int                 degree = profile->lnodes->degree;
+  int                 Nrp = degree + 1;
+  int                 Nfp = (degree + 1) * (degree + 1);
+
+  P4EST_ASSERT (degree > 1);
+
+  ncid = en[Nfp * cid + Nrp * (Nrp / 2) + (Nrp / 2)];
+  nelem = lr[ncid][1];
+
+  sc_array_init_view (&elem, lc, lr[ncid][0], nelem);
+
+  elem_to_node = P4EST_ALLOC (p4est_locidx_t *, nelem);
+
+  for (ll = 0; ll < nelem; ll++) {
+    fc[ll] = (p6est_lnodes_code_t) fc4;
+  }
+  for (k = 0, j = 0; j < Nrp; j++) {
+    for (i = 0; i < Nrp; i++, k++) {
+      nid = en[Nfp * cid + k];
+      sc_array_init_view (&node, lc, lr[nid][0], lr[nid][1]);
+      for (ll = 0; ll < nelem; ll++) {
+        elem_to_node[ll] = e_to_n +
+          (degree + 1) * (degree + 1) * (degree + 1) * ll + (degree + 1) * k;
+      }
+      if (!(i % degree) && !(j % degree)) {
+        int                 c = 2 * (! !j) + (! !i);
+
+        p6est_profile_element_to_node_single (&elem, &node, degree,
+                                              offsets[nid], elem_to_node, fc,
+                                              4 + c);
+      }
+      else if ((i % degree) && (j % degree)) {
+        p6est_profile_element_to_node_single (&elem, &elem, degree,
+                                              offsets[nid], elem_to_node,
+                                              NULL, -1);
+      }
+      else {
+        int                 f = 2 * !(j % degree) + (i == degree
+                                                     || j == degree);
+
+        p6est_profile_element_to_node_single (&elem, &node, degree,
+                                              offsets[nid], elem_to_node, fc,
+                                              f);
+      }
+    }
+  }
+  P4EST_FREE (elem_to_node);
+}
+
+void
+p6est_profile_element_to_node (p6est_t * p6est,
+                               p6est_profile_t * profile,
+                               p4est_locidx_t * offsets,
+                               p4est_locidx_t * elem_to_node,
+                               p6est_lnodes_code_t * fc)
+{
+  p4est_topidx_t      jt;
+  p4est_t            *columns = p6est->columns;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *col;
+  sc_array_t         *tquadrants;
+  p4est_locidx_t (*lr)[2] = (p4est_locidx_t (*)[2]) profile->lnode_ranges;
+  p4est_locidx_t      cid;
+  size_t              zz;
+  p6est_lnodes_code_t mask = 0x1fe0;
+  p6est_lnodes_code_t hbit = 0x0010;
+  int                 degree = profile->lnodes->degree;
+  int                 Nrp = (degree + 1);
+  int                 Nfp = (degree + 1) * (degree + 1);
+  sc_array_t         *layers = p6est->layers;
+
+  for (cid = 0, jt = columns->first_local_tree;
+       jt <= columns->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (columns->trees, jt);
+    tquadrants = &tree->quadrants;
+
+    for (zz = 0; zz < tquadrants->elem_count; ++zz, cid++) {
+      p4est_locidx_t      nlayers;
+      p4est_locidx_t      nid =
+        profile->lnodes->element_nodes[Nfp * cid + Nrp * (Nrp / 2) +
+                                       (Nrp / 2)];
+      size_t              first, last, zw, zy;
+
+      col = p4est_quadrant_array_index (tquadrants, zz);
+      P6EST_COLUMN_GET_RANGE (col, &first, &last);
+
+      nlayers = lr[nid][1];
+      p6est_profile_element_to_node_col (profile, cid, offsets,
+                                         elem_to_node, fc);
+      elem_to_node += nlayers * (degree + 1) * (degree + 1) * (degree + 1);
+
+      for (zy = 0, zw = first; zw < last; zw++, zy++) {
+        if (fc[zy] & mask) {
+          /* this layer has vertical half faces, we need to set the bit that
+           * says whether this is the upper half or the lower half */
+          p2est_quadrant_t   *layer;
+
+          layer = p2est_quadrant_array_index (layers, zw);
+
+          if (layer->z & P4EST_QUADRANT_LEN (layer->level)) {
+            /* upper half of a pair of layers */
+            fc[zy] |= hbit;
+          }
+        }
+      }
+      fc += nlayers;
+    }
+  }
+}
+
+static void
+p6est_profile_compress (p6est_profile_t * profile)
+{
+  p4est_locidx_t      nidx, il, old_off, nln =
+    profile->lnodes->num_local_nodes;
+  p4est_locidx_t (*lr)[2] = (p4est_locidx_t (*)[2]) profile->lnode_ranges;
+  sc_array_t         *lc = profile->lnode_columns;
+  size_t              old_count = lc->elem_count;
+  size_t              new_count;
+  sc_array_t         *perm;
+  size_t             *newindex;
+  size_t              zz, offset;
+
+  if (!old_count) {
+    return;
+  }
+  perm = sc_array_new_size (sizeof (size_t), old_count);
+  newindex = (size_t *) sc_array_index (perm, 0);
+
+  for (zz = 0; zz < old_count; zz++) {
+    newindex[zz] = old_count;
+  }
+
+  offset = 0;
+
+  for (nidx = 0; nidx < nln; nidx++) {
+    old_off = lr[nidx][0];
+    if (lr[nidx][1]) {
+      lr[nidx][0] = offset;
+    }
+    else {
+      P4EST_ASSERT (!lr[nidx][0]);
+    }
+    for (il = 0; il < lr[nidx][1]; il++) {
+      newindex[il + old_off] = offset++;
+    }
+  }
+  new_count = offset;
+
+  for (zz = 0; zz < old_count; zz++) {
+    if (newindex[zz] == old_count) {
+      newindex[zz] = offset++;
+    }
+  }
+
+  sc_array_permute (lc, perm, 0);
+  sc_array_destroy (perm);
+  sc_array_resize (lc, new_count);
+}
+
+p6est_profile_t    *
+p6est_profile_new_local (p6est_t * p6est,
+                         p6est_ghost_t * ghost,
+                         p6est_profile_type_t ptype,
+                         p8est_connect_type_t btype, int degree)
+{
+  p6est_profile_t    *profile = P4EST_ALLOC (p6est_profile_t, 1);
+  p4est_lnodes_t     *lnodes;
+  p4est_locidx_t      nln, nle;
+  p4est_topidx_t      jt;
+  p4est_t            *columns = p6est->columns;
+  p4est_tree_t       *tree;
+  sc_array_t         *tquadrants;
+  p4est_quadrant_t   *col;
+  size_t              first, last, count, zz, zy;
+  p4est_locidx_t     *en, (*lr)[2];
+  sc_array_t         *lc;
+  int                 i, j;
+  p2est_quadrant_t   *layer;
+  sc_array_t         *layers = p6est->layers;
+  p4est_locidx_t      nidx, enidx;
+  p4est_connect_type_t hbtype;
+  int8_t             *c;
+  sc_array_t         *thisprof;
+  sc_array_t         *selfprof;
+  sc_array_t         *faceprof;
+  sc_array_t         *cornerprof;
+  sc_array_t         *work;
+  sc_array_t          oldprof;
+  const int           Nrp = degree + 1;
+
+  P4EST_ASSERT (degree > 1);
+  profile->ptype = ptype;
+  profile->btype = btype;
+  profile->lnode_changed[0] = NULL;
+  profile->lnode_changed[1] = NULL;
+  profile->enode_counts = NULL;
+  if (btype == P8EST_CONNECT_FACE) {
+    hbtype = P4EST_CONNECT_FACE;
+  }
+  else {
+    hbtype = P4EST_CONNECT_FULL;
+  }
+  if (ghost == NULL) {
+    profile->cghost = p4est_ghost_new (p6est->columns, P4EST_CONNECT_FULL);
+    profile->ghost_owned = 1;
+  }
+  else {
+    P4EST_ASSERT (ghost->column_ghost->btype == P4EST_CONNECT_FULL);
+    profile->cghost = ghost->column_ghost;
+    profile->ghost_owned = 0;
+  }
+  if (ptype == P6EST_PROFILE_UNION) {
+    P4EST_ASSERT (degree == 2);
+  }
+  profile->lnodes = lnodes = p4est_lnodes_new (p6est->columns,
+                                               profile->cghost, degree);
+  en = lnodes->element_nodes;
+  nln = lnodes->num_local_nodes;
+  nle = lnodes->num_local_elements;
+  profile->lnode_ranges = P4EST_ALLOC_ZERO (p4est_locidx_t, 2 * nln);
+  lr = (p4est_locidx_t (*)[2]) profile->lnode_ranges;
+  profile->lnode_columns = lc = sc_array_new (sizeof (int8_t));
+  selfprof = sc_array_new (sizeof (int8_t));
+  work = sc_array_new (sizeof (int8_t));
+  faceprof = sc_array_new (sizeof (int8_t));
+  cornerprof = sc_array_new (sizeof (int8_t));
+  if (ptype == P6EST_PROFILE_UNION) {
+    profile->lnode_changed[0] = P4EST_ALLOC (p4est_locidx_t, nln);
+    profile->lnode_changed[1] = P4EST_ALLOC (p4est_locidx_t, nln);
+    profile->enode_counts = P4EST_ALLOC (p4est_locidx_t, P4EST_INSUL * nle);
+    profile->evenodd = 0;
+    memset (profile->lnode_changed[0], -1, nln * sizeof (int));
+  }
+
+  /* create the profiles for each node: layers are reduced to just their level
+   * */
+  for (enidx = 0, jt = columns->first_local_tree;
+       jt <= columns->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (columns->trees, jt);
+    tquadrants = &tree->quadrants;
+
+    for (zz = 0; zz < tquadrants->elem_count; ++zz) {
+      col = p4est_quadrant_array_index (tquadrants, zz);
+      P6EST_COLUMN_GET_RANGE (col, &first, &last);
+      count = last - first;
+      sc_array_truncate (selfprof);
+      c = (int8_t *) sc_array_push_count (selfprof, count);
+      for (zy = first; zy < last; zy++) {
+        layer = p2est_quadrant_array_index (layers, zy);
+        *(c++) = layer->level;
+      }
+      if (ptype == P6EST_PROFILE_UNION) {
+        p6est_profile_balance_self (selfprof, work);
+        if (btype == P8EST_CONNECT_FACE) {
+          p6est_profile_balance_face (selfprof, faceprof, work);
+        }
+        else {
+          p6est_profile_balance_full (selfprof, faceprof, work);
+        }
+        if (btype == P8EST_CONNECT_EDGE) {
+          p6est_profile_balance_face (selfprof, cornerprof, work);
+        }
+        else if (btype == P8EST_CONNECT_FULL) {
+          p6est_profile_balance_full (selfprof, cornerprof, work);
+        }
+      }
+      for (j = 0; j < Nrp; j++) {
+        for (i = 0; i < Nrp; i++, enidx++) {
+          nidx = en[enidx];
+          if (ptype == P6EST_PROFILE_UNION) {
+            thisprof = NULL;
+            if (!(i % degree) && !(j % degree)) {
+              if (hbtype == P4EST_CONNECT_FACE) {
+                /* skip corners if we don't need to balance them */
+                P4EST_ASSERT (!lr[nidx][0]);
+                P4EST_ASSERT (!lr[nidx][1]);
+                continue;
+              }
+              else {
+                thisprof = cornerprof;
+              }
+            }
+            else if ((i % degree) && (j % degree)) {
+              thisprof = selfprof;
+            }
+            else {
+              thisprof = faceprof;
+            }
+            count = thisprof->elem_count;
+            profile->enode_counts[enidx] = count;
+            if (!lr[nidx][1]) {
+              /* if this node has not yet been initialized, initialize it */
+              lr[nidx][0] = lc->elem_count;
+              lr[nidx][1] = count;
+              c = (int8_t *) sc_array_push_count (lc, count);
+              memcpy (c, thisprof->array, count * sizeof (int8_t));
+            }
+            else {
+              /* if this node has been initialized, combine the two profiles,
+               * taking the finer layers from each */
+              sc_array_init_view (&oldprof, lc, lr[nidx][0], lr[nidx][1]);
+              p6est_profile_union (thisprof, &oldprof, work);
+              if (work->elem_count > oldprof.elem_count) {
+                lr[nidx][0] = lc->elem_count;
+                lr[nidx][1] = work->elem_count;
+                c = (int8_t *) sc_array_push_count (lc, work->elem_count);
+                memcpy (c, work->array, work->elem_count * work->elem_size);
+              }
+            }
+          }
+          else {
+            count = selfprof->elem_count;
+            if (!lr[nidx][1]) {
+              /* if this node has not yet been initialized, initialize it */
+              lr[nidx][0] = lc->elem_count;
+              lr[nidx][1] = count;
+              c = (int8_t *) sc_array_push_count (lc, count);
+              memcpy (c, selfprof->array, count * sizeof (int8_t));
+            }
+            else {
+              /* if this node has been initialized, combine the two profiles,
+               * taking the coarser layers from each */
+              sc_array_init_view (&oldprof, lc, lr[nidx][0], lr[nidx][1]);
+              p6est_profile_intersection (selfprof, &oldprof, work);
+              P4EST_ASSERT (work->elem_count <= oldprof.elem_count);
+              if (work->elem_count < oldprof.elem_count) {
+                lr[nidx][1] = work->elem_count;
+                memcpy (oldprof.array, work->array,
+                        work->elem_count * work->elem_size);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  p6est_profile_compress (profile);
+
+  sc_array_destroy (selfprof);
+  sc_array_destroy (faceprof);
+  sc_array_destroy (cornerprof);
+  sc_array_destroy (work);
+
+  return profile;
+}
+
+void
+p6est_profile_balance_local (p6est_profile_t * profile)
+{
+  p4est_lnodes_t     *lnodes = profile->lnodes;
+  p4est_locidx_t      nln, nle;
+  p4est_locidx_t     *en, (*lr)[2];
+  sc_array_t         *lc;
+  int                 i, j;
+  p4est_locidx_t      nidx, enidx, eidx;
+  p8est_connect_type_t btype = profile->btype;
+  p4est_connect_type_t hbtype;
+  int8_t             *c;
+  sc_array_t         *thisprof;
+  sc_array_t         *selfprof;
+  sc_array_t         *faceprof;
+  sc_array_t         *cornerprof;
+  sc_array_t         *work;
+  sc_array_t          oldprof;
+  sc_array_t          testprof;
+  int                 any_prof_change;
+  int                 any_local_change;
+  int                 evenodd = profile->evenodd;
+
+  P4EST_ASSERT (profile->lnodes->degree == 2);
+
+  if (btype == P8EST_CONNECT_FACE) {
+    hbtype = P4EST_CONNECT_FACE;
+  }
+  else {
+    hbtype = P4EST_CONNECT_FULL;
+  }
+  en = lnodes->element_nodes;
+  nln = lnodes->num_local_nodes;
+  nle = lnodes->num_local_elements;
+  lr = (p4est_locidx_t (*)[2]) profile->lnode_ranges;
+  lc = profile->lnode_columns;
+  selfprof = sc_array_new (sizeof (int8_t));
+  work = sc_array_new (sizeof (int8_t));
+  faceprof = sc_array_new (sizeof (int8_t));
+  cornerprof = sc_array_new (sizeof (int8_t));
+
+  do {
+    /* We read from evenodd and write to evenodd ^ 1 */
+    memset (&(profile->lnode_changed[evenodd ^ 1][0]), 0, sizeof (int) * nln);
+    P4EST_GLOBAL_VERBOSE ("p6est_balance local loop\n");
+
+    any_local_change = 0;
+    for (eidx = 0, enidx = 0; eidx < nle; eidx++) {
+      p4est_locidx_t      start_enidx = enidx;
+      nidx = en[start_enidx + P4EST_INSUL / 2];
+      P4EST_ASSERT (lr[nidx][1]);
+      sc_array_init_view (&oldprof, lc, lr[nidx][0], lr[nidx][1]);
+      thisprof = &oldprof;
+      any_prof_change = 0;
+      for (j = 0; j < 3; j++) {
+        for (i = 0; i < 3; i++, enidx++) {
+          nidx = en[enidx];
+          if (!profile->lnode_changed[evenodd][nidx]) {
+            /* if the profile hasn't changed since I wrote to it, there's no
+             * need to balance against it */
+            continue;
+          }
+          if (i != 1 && j != 1) {
+            if (hbtype == P4EST_CONNECT_FACE) {
+              /* skip corners if we don't need to balance them */
+              P4EST_ASSERT (!lr[nidx][0]);
+              P4EST_ASSERT (!lr[nidx][1]);
+              continue;
+            }
+          }
+          if (i == 1 && j == 1) {
+            /* no need to further balance against oneself */
+            continue;
+          }
+          P4EST_ASSERT (lr[nidx][1]);
+          P4EST_ASSERT (profile->enode_counts[enidx] <= lr[nidx][1]);
+          if (profile->enode_counts[enidx] == lr[nidx][1]) {
+            /* if the profile hasn't changed since I wrote to it, there's no
+             * need to balance against it */
+            continue;
+          }
+          sc_array_init_view (&testprof, lc, lr[nidx][0], lr[nidx][1]);
+          p6est_profile_union (thisprof, &testprof, work);
+          if (work->elem_count > thisprof->elem_count) {
+            P4EST_ASSERT (profile->lnode_changed[evenodd][nidx]);
+            any_prof_change = 1;
+            sc_array_copy (selfprof, work);
+            thisprof = selfprof;
+          }
+        }
+      }
+
+      if (any_prof_change) {
+        P4EST_ASSERT (thisprof == selfprof);
+        P4EST_ASSERT (selfprof->elem_count > oldprof.elem_count);
+        /* update */
+        if (btype == P8EST_CONNECT_FACE) {
+          p6est_profile_balance_face (selfprof, faceprof, work);
+        }
+        else {
+          p6est_profile_balance_full (selfprof, faceprof, work);
+        }
+        if (btype == P8EST_CONNECT_EDGE) {
+          p6est_profile_balance_face (selfprof, cornerprof, work);
+        }
+        else if (btype == P8EST_CONNECT_FULL) {
+          p6est_profile_balance_full (selfprof, cornerprof, work);
+        }
+        enidx = start_enidx;
+        for (j = 0; j < 3; j++) {
+          for (i = 0; i < 3; i++, enidx++) {
+            thisprof = NULL;
+            nidx = en[enidx];
+            if (i != 1 && j != 1) {
+              if (hbtype == P4EST_CONNECT_FACE) {
+                /* skip corners if we don't need to balance them */
+                P4EST_ASSERT (!lr[nidx][0]);
+                P4EST_ASSERT (!lr[nidx][1]);
+                continue;
+              }
+              else {
+                thisprof = cornerprof;
+              }
+            }
+            else if (i == 1 && j == 1) {
+              thisprof = selfprof;
+            }
+            else {
+              thisprof = faceprof;
+            }
+            P4EST_ASSERT (lr[nidx][1]);
+            /* if this node has been initialized, combine the two profiles,
+             * taking the finer layers from each */
+            sc_array_init_view (&oldprof, lc, lr[nidx][0], lr[nidx][1]);
+            if (i == 1 && j == 1) {
+              sc_array_copy (work, thisprof);
+            }
+            else {
+              p6est_profile_union (thisprof, &oldprof, work);
+            }
+            if (work->elem_count > oldprof.elem_count) {
+              if (!(i == 1 && j == 1)) {        /* we don't count changing self */
+                profile->lnode_changed[evenodd ^ 1][nidx] = 1;
+                any_local_change = 1;
+              }
+              lr[nidx][0] = lc->elem_count;
+              lr[nidx][1] = work->elem_count;
+              c = (int8_t *) sc_array_push_count (lc, work->elem_count);
+              memcpy (c, work->array, work->elem_count * work->elem_size);
+            }
+            profile->enode_counts[enidx] = lr[nidx][1];
+          }
+        }
+      }
+    }
+    p6est_profile_compress (profile);
+    evenodd ^= 1;
+  } while (any_local_change);
+
+  profile->evenodd = evenodd;
+  sc_array_destroy (selfprof);
+  sc_array_destroy (faceprof);
+  sc_array_destroy (cornerprof);
+  sc_array_destroy (work);
+}
+
+int
+p6est_profile_sync (p6est_profile_t * profile)
+{
+  p4est_lnodes_t     *lnodes = profile->lnodes;
+  p4est_locidx_t      nln = lnodes->num_local_nodes;
+  sc_array_t          lrview;
+  p4est_lnodes_buffer_t *countbuf;
+  sc_array_t         *sharers;
+  size_t              zz, nsharers;
+  int                 nleft;
+  int8_t             *recv, *send;
+  int                *array_of_indices;
+  p4est_locidx_t      recv_total;
+  p4est_locidx_t     *recv_offsets, recv_offset;
+  p4est_locidx_t      send_total;
+  p4est_locidx_t     *send_offsets, send_offset;
+  p4est_locidx_t (*lr)[2];
+  sc_array_t         *lc = profile->lnode_columns;
+  sc_MPI_Request     *recv_request, *send_request;
+  sc_array_t         *work;
+  int                 any_change = 0;
+  int                 any_global_change;
+  int                 mpiret, mpirank;
+  int                 evenodd = profile->evenodd;
+
+  lr = (p4est_locidx_t (*)[2]) profile->lnode_ranges;
+  sharers = lnodes->sharers;
+  nsharers = sharers->elem_count;
+
+  mpiret = sc_MPI_Comm_rank (lnodes->mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_array_init_data (&lrview, lr, 2 * sizeof (p4est_locidx_t), nln);
+
+  countbuf = p4est_lnodes_share_all_begin (&lrview, lnodes);
+  send_offsets = P4EST_ALLOC (p4est_locidx_t, nsharers + 1);
+  send_offset = 0;
+  for (zz = 0; zz < nsharers; zz++) {
+    p4est_lnodes_rank_t *sharer;
+    sc_array_t         *send_buf;
+    size_t              zy, nnodes;
+
+    send_offsets[zz] = send_offset;
+    sharer = p4est_lnodes_rank_array_index (sharers, zz);
+    if (sharer->rank == mpirank) {
+      continue;
+    }
+    send_buf = (sc_array_t *) sc_array_index (countbuf->send_buffers, zz);
+    nnodes = sharer->shared_nodes.elem_count;
+
+    P4EST_ASSERT (nnodes == send_buf->elem_count);
+
+    P4EST_ASSERT (send_buf->elem_size == 2 * sizeof (p4est_locidx_t));
+    for (zy = 0; zy < nnodes; zy++) {
+      p4est_locidx_t     *lp =
+        (p4est_locidx_t *) sc_array_index (send_buf, zy);
+      P4EST_ASSERT (lp[0] >= 0);
+      P4EST_ASSERT (lp[1] >= 0);
+      send_offset += lp[1];
+    }
+  }
+  send_total = send_offsets[nsharers] = send_offset;
+
+  p4est_lnodes_share_all_end (countbuf);
+  recv_offsets = P4EST_ALLOC (p4est_locidx_t, nsharers + 1);
+  recv_offset = 0;
+  for (zz = 0; zz < nsharers; zz++) {
+    p4est_lnodes_rank_t *sharer;
+    sc_array_t         *recv_buf;
+    size_t              zy, nnodes;
+
+    recv_offsets[zz] = recv_offset;
+    sharer = p4est_lnodes_rank_array_index (sharers, zz);
+    if (sharer->rank == mpirank) {
+      continue;
+    }
+    recv_buf = (sc_array_t *) sc_array_index (countbuf->recv_buffers, zz);
+    nnodes = sharer->shared_nodes.elem_count;
+
+    P4EST_ASSERT (nnodes == recv_buf->elem_count);
+
+    P4EST_ASSERT (recv_buf->elem_size == 2 * sizeof (p4est_locidx_t));
+    for (zy = 0; zy < nnodes; zy++) {
+      p4est_locidx_t     *lp =
+        (p4est_locidx_t *) sc_array_index (recv_buf, zy);
+      P4EST_ASSERT (lp[0] >= 0);
+      P4EST_ASSERT (lp[1] >= 0);
+      recv_offset += lp[1];
+    }
+  }
+  recv_total = recv_offsets[nsharers] = recv_offset;
+
+  recv = P4EST_ALLOC (int8_t, recv_total);
+  recv_request = P4EST_ALLOC (sc_MPI_Request, nsharers);
+  send = P4EST_ALLOC (int8_t, send_total);
+  send_request = P4EST_ALLOC (sc_MPI_Request, nsharers);
+
+  /* post receives */
+  nleft = 0;
+  for (zz = 0; zz < nsharers; zz++) {
+    p4est_lnodes_rank_t *sharer;
+    int                 icount = recv_offsets[zz + 1] - recv_offsets[zz];
+
+    sharer = p4est_lnodes_rank_array_index (sharers, zz);
+    if (sharer->rank == mpirank) {
+      recv_request[zz] = sc_MPI_REQUEST_NULL;
+      continue;
+    }
+    if (icount) {
+      mpiret =
+        sc_MPI_Irecv (recv + recv_offsets[zz], icount * sizeof (int8_t),
+                      sc_MPI_BYTE, sharer->rank, P6EST_COMM_BALANCE,
+                      lnodes->mpicomm, recv_request + zz);
+      SC_CHECK_MPI (mpiret);
+      nleft++;
+    }
+    else {
+      recv_request[zz] = sc_MPI_REQUEST_NULL;
+    }
+  }
+
+  /* post sends */
+  for (zz = 0; zz < nsharers; zz++) {
+    p4est_lnodes_rank_t *sharer;
+    size_t              zy, nnodes;
+    int                 icount;
+    sc_array_t         *shared_nodes;
+
+    sharer = p4est_lnodes_rank_array_index (sharers, zz);
+    if (sharer->rank == mpirank) {
+      send_request[zz] = sc_MPI_REQUEST_NULL;
+      continue;
+    }
+    shared_nodes = &sharer->shared_nodes;
+    nnodes = shared_nodes->elem_count;
+    icount = 0;
+    for (zy = 0; zy < nnodes; zy++) {
+      p4est_locidx_t      nidx;
+      int8_t             *c;
+
+      nidx = *((p4est_locidx_t *) sc_array_index (shared_nodes, zy));
+
+      if (lr[nidx][1]) {
+        c = (int8_t *) sc_array_index (lc, lr[nidx][0]);
+        memcpy (send + send_offsets[zz] + icount, c,
+                lr[nidx][1] * sizeof (int8_t));
+        icount += lr[nidx][1];
+      }
+      else {
+        P4EST_ASSERT (!lr[nidx][0]);
+      }
+    }
+    P4EST_ASSERT (icount == send_offsets[zz + 1] - send_offsets[zz]);
+    if (icount) {
+      mpiret =
+        sc_MPI_Isend (send + send_offsets[zz], icount * sizeof (int8_t),
+                      sc_MPI_BYTE, sharer->rank, P6EST_COMM_BALANCE,
+                      lnodes->mpicomm, send_request + zz);
+      SC_CHECK_MPI (mpiret);
+    }
+    else {
+      send_request[zz] = sc_MPI_REQUEST_NULL;
+    }
+  }
+
+  work = sc_array_new (sizeof (int8_t));
+  array_of_indices = P4EST_ALLOC (int, nsharers);
+  while (nleft) {
+    int                 outcount;
+    int                 i;
+
+    mpiret = sc_MPI_Waitsome (nsharers, recv_request, &outcount,
+                              array_of_indices, sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+
+    for (i = 0; i < outcount; i++) {
+      p4est_lnodes_rank_t *sharer;
+      size_t              zy, nnode;
+      sc_array_t         *shared_nodes;
+      sc_array_t         *recv_buf;
+
+      zz = array_of_indices[i];
+      sharer = p4est_lnodes_rank_array_index (sharers, zz);
+      shared_nodes = &sharer->shared_nodes;
+      recv_buf = (sc_array_t *) sc_array_index (countbuf->recv_buffers, zz);
+      nnode = shared_nodes->elem_count;
+      P4EST_ASSERT (nnode == recv_buf->elem_count);
+
+      recv_offset = recv_offsets[zz];
+      for (zy = 0; zy < nnode; zy++) {
+        p4est_locidx_t     *lp;
+        p4est_locidx_t      nidx;
+        sc_array_t          oldview, newview;
+
+        nidx = *((p4est_locidx_t *) sc_array_index (shared_nodes, zy));
+        lp = (p4est_locidx_t *) sc_array_index (recv_buf, zy);
+
+        sc_array_init_view (&oldview, lc, lr[nidx][0], lr[nidx][1]);
+        sc_array_init_data (&newview, recv + recv_offset, sizeof (int8_t),
+                            lp[1]);
+        if (profile->ptype == P6EST_PROFILE_UNION) {
+          p6est_profile_union (&oldview, &newview, work);
+
+          if (work->elem_count > oldview.elem_count) {
+            int8_t             *c;
+
+            any_change = 1;
+            lr[nidx][0] = lc->elem_count;
+            lr[nidx][1] = work->elem_count;
+            profile->lnode_changed[evenodd][nidx] = 1;
+
+            c = (int8_t *) sc_array_push_count (lc, work->elem_count);
+            memcpy (c, work->array, work->elem_count * work->elem_size);
+          }
+        }
+        else {
+          p6est_profile_intersection (&oldview, &newview, work);
+          P4EST_ASSERT (work->elem_count <= oldview.elem_count);
+          if (work->elem_count < oldview.elem_count) {
+            lr[nidx][1] = work->elem_count;
+            memcpy (oldview.array, work->array,
+                    work->elem_count * work->elem_size);
+          }
+        }
+
+        recv_offset += lp[1];
+      }
+      P4EST_ASSERT (recv_offset == recv_offsets[zz + 1]);
+    }
+
+    nleft -= outcount;
+    P4EST_ASSERT (nleft >= 0);
+  }
+  P4EST_FREE (array_of_indices);
+  sc_array_destroy (work);
+
+  p6est_profile_compress (profile);
+  p4est_lnodes_buffer_destroy (countbuf);
+
+  P4EST_FREE (recv_request);
+  P4EST_FREE (recv_offsets);
+  P4EST_FREE (recv);
+
+  {
+    mpiret = sc_MPI_Waitall (nsharers, send_request, sc_MPI_STATUSES_IGNORE);
+
+    SC_CHECK_MPI (mpiret);
+    P4EST_FREE (send_request);
+    P4EST_FREE (send_offsets);
+    P4EST_FREE (send);
+
+    any_global_change = any_change;
+    mpiret = sc_MPI_Allreduce (&any_change, &any_global_change, 1, sc_MPI_INT,
+                               sc_MPI_LOR, lnodes->mpicomm);
+
+    SC_CHECK_MPI (mpiret);
+  }
+
+  return any_global_change;
+}
+
+void
+p6est_profile_destroy (p6est_profile_t * profile)
+{
+  p4est_lnodes_destroy (profile->lnodes);
+  if (profile->ghost_owned) {
+    p4est_ghost_destroy (profile->cghost);
+  }
+  if (profile->lnode_changed[0]) {
+    P4EST_ASSERT (profile->lnode_changed[1]);
+    P4EST_FREE (profile->lnode_changed[0]);
+    P4EST_FREE (profile->lnode_changed[1]);
+    P4EST_ASSERT (profile->enode_counts);
+    P4EST_FREE (profile->enode_counts);
+  }
+  P4EST_FREE (profile->lnode_ranges);
+  sc_array_destroy (profile->lnode_columns);
+  P4EST_FREE (profile);
+}
+
+void
+p6est_refine_to_profile (p6est_t * p6est, p6est_profile_t * profile,
+                         p6est_init_t init_fn, p6est_replace_t replace_fn)
+{
+  size_t              zz, zy, first, last;
+  p4est_topidx_t      jt;
+  p4est_quadrant_t   *col;
+  p4est_tree_t       *tree;
+  sc_array_t         *tquadrants;
+  p4est_locidx_t      eidx;
+  p4est_locidx_t     *en = profile->lnodes->element_nodes;
+  p4est_locidx_t (*lr)[2];
+  p4est_locidx_t      nidx, pidx, pfirst, plast;
+  sc_array_t         *layers = p6est->layers;
+  sc_array_t         *lc = profile->lnode_columns;
+  sc_array_t         *work;
+
+  P4EST_ASSERT (profile->lnodes->degree == 2);
+
+  lr = (p4est_locidx_t (*)[2]) profile->lnode_ranges;
+  work = sc_array_new (sizeof (p2est_quadrant_t));
+  for (eidx = 0, jt = p6est->columns->first_local_tree;
+       jt <= p6est->columns->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (p6est->columns->trees, jt);
+    tquadrants = &tree->quadrants;
+    for (zz = 0; zz < tquadrants->elem_count; ++zz, eidx++) {
+
+      col = p4est_quadrant_array_index (tquadrants, zz);
+      P6EST_COLUMN_GET_RANGE (col, &first, &last);
+      nidx = en[P4EST_INSUL * eidx + P4EST_INSUL / 2];
+      P4EST_ASSERT ((size_t) lr[nidx][1] >= last - first);
+      pfirst = lr[nidx][0];
+      plast = pfirst + lr[nidx][1];
+      if ((size_t) lr[nidx][1] > last - first) {
+        p2est_quadrant_t    stack[P4EST_QMAXLEVEL];
+        p2est_quadrant_t   *q, *r, s, t;
+        int                 stackcount;
+
+        sc_array_truncate (work);
+        stackcount = 0;
+        zy = first;
+        for (pidx = pfirst; pidx < plast; pidx++) {
+          int8_t              p;
+
+          P4EST_ASSERT (stackcount || zy < last);
+
+          p = *((int8_t *) sc_array_index (lc, pidx));
+
+          if (stackcount) {
+            q = &(stack[--stackcount]);
+          }
+          else {
+            q = p2est_quadrant_array_index (layers, zy++);
+          }
+
+          P4EST_ASSERT (q->level <= p);
+          while (q->level < p) {
+            p2est_quadrant_t   *child[2];
+
+            t = *q;
+            s = *q;
+            s.level++;
+            stack[stackcount] = s;
+            stack[stackcount].z += P4EST_QUADRANT_LEN (s.level);
+            child[0] = &s;
+            child[1] = &stack[stackcount++];
+            p6est_layer_init_data (p6est, jt, col, child[0], init_fn);
+            p6est_layer_init_data (p6est, jt, col, child[1], init_fn);
+            q = &t;
+            if (replace_fn) {
+              replace_fn (p6est, jt, 1, 1, &col, &q, 1, 2, &col, child);
+            }
+            p6est_layer_free_data (p6est, &t);
+            q = &s;
+          }
+          r = p2est_quadrant_array_push (work);
+          *r = *q;
+        }
+        P4EST_ASSERT (work->elem_count == (size_t) lr[nidx][1]);
+        first = layers->elem_count;
+        last = first + work->elem_count;
+        P6EST_COLUMN_SET_RANGE (col, first, last);
+        q = (p2est_quadrant_t *) sc_array_push_count (layers,
+                                                      work->elem_count);
+        memcpy (q, work->array, work->elem_count * work->elem_size);
+      }
+    }
+  }
+  sc_array_destroy (work);
+  p6est_compress_columns (p6est);
+  p6est_update_offsets (p6est);
+}
diff --git a/example/p6est/p6est_profile.h b/example/p6est/p6est_profile.h
new file mode 100644
index 0000000..88469ec
--- /dev/null
+++ b/example/p6est/p6est_profile.h
@@ -0,0 +1,105 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2014 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P6EST_PROFILE_H
+#define P6EST_PROFILE_H
+
+#include <p6est.h>
+#include <p6est_ghost.h>
+#include <p4est_lnodes.h>
+#include <p6est_lnodes.h>
+
+/** A p6est profile is used to (a) balance a p6est, and (b) generate a
+ * p6est_lnodes.  In every case, layers in a column are compressed to one
+ * int8_t each.  The resulting column profiles and be quickly intersected,
+ * unioned and balanced.  We use a p4est_lnodes_t to communicate between
+ * neighboring columns, and a p4est_ghost_t to communicate between neighboring
+ * processes.
+ */
+
+typedef enum
+{
+  P6EST_PROFILE_UNION,
+  P6EST_PROFILE_INTERSECTION
+}
+p6est_profile_type_t;
+
+typedef struct p6est_profile
+{
+  p6est_profile_type_t ptype;
+  p8est_connect_type_t btype;
+  p4est_lnodes_t     *lnodes;
+  p4est_ghost_t      *cghost;
+  int                 ghost_owned;
+  p4est_locidx_t     *lnode_ranges;
+  sc_array_t         *lnode_columns;
+  int                *lnode_changed[2];
+  p4est_locidx_t     *enode_counts;
+  int                 evenodd;
+}
+p6est_profile_t;
+
+/** Create a new profile.
+ * \param[in] p6est
+ * \param[in] ghost optional, can be NULL
+ * \param[in] ptype P6EST_PROFILE_UNION if we are balancing,
+ *                  P6EST_PROFILE_INTERSECTION if we are generating lnodes
+ * \param[in] btype Type of 3D balance law desired.
+ * \param[in] degree degree of underlying lnodes, should be 2 if used for
+ *                   balancing
+ */
+
+p6est_profile_t    *p6est_profile_new_local (p6est_t * p6est,
+                                             p6est_ghost_t * ghost,
+                                             p6est_profile_type_t ptype,
+                                             p8est_connect_type_t btype,
+                                             int degree);
+
+/** Destroy a profile */
+void                p6est_profile_destroy (p6est_profile_t * profile);
+
+/** Enforce balance between the column profiles locally: no communication */
+void                p6est_profile_balance_local (p6est_profile_t * profile);
+
+/** Synchronize the data from other processors, taking unions or
+ * intersections, as determined at profile creation in \a
+ * p6est_profile_new_local
+ *
+ * \return whether any change has occured.
+ * */
+int                 p6est_profile_sync (p6est_profile_t * profile);
+
+/** modify a p6est, whose columns match the column profiles, to match the
+ * column profiles */
+void                p6est_refine_to_profile (p6est_t * p6est,
+                                             p6est_profile_t * profile,
+                                             p6est_init_t init_fn,
+                                             p6est_replace_t replace_fn);
+
+void                p6est_profile_element_to_node (p6est_t * p6est,
+                                                   p6est_profile_t * profile,
+                                                   p4est_locidx_t * offsets,
+                                                   p4est_locidx_t *
+                                                   elem_to_node,
+                                                   p6est_lnodes_code_t * fc);
+#endif /* !P6EST_PROFILE_H */
diff --git a/example/p6est/p6est_vtk.c b/example/p6est/p6est_vtk.c
new file mode 100644
index 0000000..86d0f1b
--- /dev/null
+++ b/example/p6est/p6est_vtk.c
@@ -0,0 +1,775 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2013 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p6est_vtk.h>
+#include <sc_io.h>
+
+#define P4EST_VTK_CELL_TYPE     11      /* VTK_VOXEL */
+
+static const double p6est_vtk_scale = 1.;
+static const int    p6est_vtk_write_tree = 1;
+static const int    p6est_vtk_write_rank = 1;
+static const int    p6est_vtk_wrap_rank = 0;
+
+#ifndef P4EST_VTK_DOUBLES
+#define P4EST_VTK_FLOAT_NAME "Float32"
+#define P4EST_VTK_FLOAT_TYPE float
+#else
+#define P4EST_VTK_FLOAT_NAME "Float64"
+#define P4EST_VTK_FLOAT_TYPE double
+#endif
+
+#ifndef P4EST_VTK_BINARY
+#define P4EST_VTK_ASCII 1
+#define P4EST_VTK_FORMAT_STRING "ascii"
+#else
+#define P4EST_VTK_FORMAT_STRING "binary"
+
+static int
+p6est_vtk_write_binary (FILE * vtkfile, char *numeric_data,
+                        size_t byte_length)
+{
+#ifndef P4EST_VTK_COMPRESSION
+  return sc_vtk_write_binary (vtkfile, numeric_data, byte_length);
+#else
+  return sc_vtk_write_compressed (vtkfile, numeric_data, byte_length);
+#endif /* P4EST_VTK_COMPRESSION */
+}
+
+#endif /* P4EST_VTK_BINARY */
+
+void
+p6est_vtk_write_file (p6est_t * p6est, const char *filename)
+{
+  p6est_vtk_write_all (p6est,
+                       p6est_vtk_scale, p6est_vtk_write_tree,
+                       p6est_vtk_write_rank, p6est_vtk_wrap_rank,
+                       0, 0, filename);
+}
+
+void
+p6est_vtk_write_all (p6est_t * p6est,
+                     double scale, int write_tree,
+                     int write_rank, int wrap_rank,
+                     int num_scalars, int num_vectors,
+                     const char *filename, ...)
+{
+  int                 retval;
+  int                 i, all;
+  int                 scalar_strlen, vector_strlen;
+  char                point_scalars[BUFSIZ], point_vectors[BUFSIZ];
+  const char         *name, **names;
+  double            **values;
+  va_list             ap;
+
+  P4EST_ASSERT (num_scalars >= 0 && num_vectors >= 0);
+
+  values = P4EST_ALLOC (double *, num_scalars + num_vectors);
+  names = P4EST_ALLOC (const char *, num_scalars + num_vectors);
+
+  va_start (ap, filename);
+  all = 0;
+  scalar_strlen = 0;
+  point_scalars[0] = '\0';
+  for (i = 0; i < num_scalars; ++all, ++i) {
+    name = names[all] = va_arg (ap, const char *);
+    retval = snprintf (point_scalars + scalar_strlen, BUFSIZ - scalar_strlen,
+                       "%s%s", i == 0 ? "" : ",", name);
+    SC_CHECK_ABORT (retval > 0, "p6est_vtk: Error collecting point scalars");
+    scalar_strlen += retval;
+    values[all] = va_arg (ap, double *);
+  }
+  vector_strlen = 0;
+  point_vectors[0] = '\0';
+  for (i = 0; i < num_vectors; ++all, ++i) {
+    name = names[all] = va_arg (ap, const char *);
+    retval = snprintf (point_vectors + vector_strlen, BUFSIZ - vector_strlen,
+                       "%s%s", i == 0 ? "" : ",", name);
+    SC_CHECK_ABORT (retval > 0, "p6est_vtk: Error collecting point vectors");
+    vector_strlen += retval;
+    values[all] = va_arg (ap, double *);
+  }
+  va_end (ap);
+
+  retval = p6est_vtk_write_header (p6est, scale,
+                                   write_tree, write_rank, wrap_rank,
+                                   num_scalars > 0 ? point_scalars : NULL,
+                                   num_vectors > 0 ? point_vectors : NULL,
+                                   filename);
+  SC_CHECK_ABORT (!retval, "p6est_vtk: Error writing header");
+
+  all = 0;
+  for (i = 0; i < num_scalars; ++all, ++i) {
+    retval = p6est_vtk_write_point_scalar (p6est, filename,
+                                           names[all], values[all]);
+    SC_CHECK_ABORT (!retval, "p6est_vtk: Error writing point scalars");
+  }
+  for (i = 0; i < num_vectors; ++all, ++i) {
+    retval = p6est_vtk_write_point_vector (p6est, filename,
+                                           names[all], values[all]);
+    SC_CHECK_ABORT (!retval, "p6est_vtk: Error writing point vectors");
+  }
+
+  retval = p6est_vtk_write_footer (p6est, filename);
+  SC_CHECK_ABORT (!retval, "p6est_vtk: Error writing footer");
+
+  P4EST_FREE (values);
+  P4EST_FREE (names);
+}
+
+int
+p6est_vtk_write_header (p6est_t * p6est,
+                        double scale, int write_tree, int write_rank,
+                        int wrap_rank, const char *point_scalars,
+                        const char *point_vectors, const char *filename)
+{
+  p6est_connectivity_t *connectivity = p6est->connectivity;
+  p4est_t            *p4est = p6est->columns;
+  sc_array_t         *layers = p6est->layers;
+  sc_array_t         *trees = p4est->trees;
+  const int           mpirank = p4est->mpirank;
+  const double        intsize = 1.0 / P4EST_ROOT_LEN;
+  double              v[24];
+  const p4est_topidx_t first_local_tree = p4est->first_local_tree;
+  const p4est_topidx_t last_local_tree = p4est->last_local_tree;
+  const p4est_locidx_t Ncells = (p4est_locidx_t) layers->elem_count;
+  const p4est_locidx_t Ncorners = P8EST_CHILDREN * Ncells;
+#ifdef P4EST_VTK_ASCII
+  double              wx, wy, wz;
+  p4est_locidx_t      sk;
+#else
+  int                 retval;
+  uint8_t            *uint8_data;
+  p4est_locidx_t     *locidx_data;
+#endif
+  int                 xi, yi, j, k;
+  int                 zi;
+  double              h2, h2z, eta_x, eta_y, eta_z = 0.;
+  double              xyz[3];   /* 3 not P4EST_DIM */
+  size_t              num_cols, zz, zy, first, last;
+  p4est_topidx_t      jt;
+  p4est_locidx_t      quad_count, Ntotal;
+  p4est_locidx_t      il;
+  P4EST_VTK_FLOAT_TYPE *float_data;
+  sc_array_t         *columns;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *col;
+  p2est_quadrant_t   *layer;
+  char                vtufilename[BUFSIZ];
+  FILE               *vtufile;
+
+  SC_CHECK_ABORT (connectivity->conn4->num_vertices > 0,
+                  "Must provide connectivity with vertex information");
+
+  P4EST_ASSERT (0. <= scale && scale <= 1. && wrap_rank >= 0);
+
+  Ntotal = Ncorners;
+  if (scale == 1.) {
+    scale = 1. - 2. * SC_EPS;
+    P4EST_ASSERT (scale < 1.);
+  }
+
+  /* Have each proc write to its own file */
+  snprintf (vtufilename, BUFSIZ, "%s_%04d.vtu", filename, mpirank);
+  /* Use "w" for writing the initial part of the file.
+   * For further parts, use "r+" and fseek so write_compressed succeeds.
+   */
+  vtufile = fopen (vtufilename, "wb");
+  if (vtufile == NULL) {
+    P4EST_LERRORF ("Could not open %s for output\n", vtufilename);
+    return -1;
+  }
+
+  fprintf (vtufile, "<?xml version=\"1.0\"?>\n");
+  fprintf (vtufile, "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"");
+#if defined P4EST_VTK_BINARY && defined P4EST_VTK_COMPRESSION
+  fprintf (vtufile, " compressor=\"vtkZLibDataCompressor\"");
+#endif
+#ifdef SC_IS_BIGENDIAN
+  fprintf (vtufile, " byte_order=\"BigEndian\">\n");
+#else
+  fprintf (vtufile, " byte_order=\"LittleEndian\">\n");
+#endif
+  fprintf (vtufile, "  <UnstructuredGrid>\n");
+  fprintf (vtufile,
+           "    <Piece NumberOfPoints=\"%lld\" NumberOfCells=\"%lld\">\n",
+           (long long) Ntotal, (long long) Ncells);
+  fprintf (vtufile, "      <Points>\n");
+
+  float_data = P4EST_ALLOC (P4EST_VTK_FLOAT_TYPE, 3 * Ntotal);
+
+  /* write point position data */
+  fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"Position\""
+           " NumberOfComponents=\"3\" format=\"%s\">\n",
+           P4EST_VTK_FLOAT_NAME, P4EST_VTK_FORMAT_STRING);
+
+  /* loop over the trees */
+  for (jt = first_local_tree, quad_count = 0; jt <= last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (trees, jt);
+    columns = &tree->quadrants;
+    num_cols = columns->elem_count;
+    p6est_tree_get_vertices (connectivity, jt, v);
+
+    /* loop over the elements in tree and calculated vertex coordinates */
+    for (zz = 0; zz < num_cols; ++zz) {
+      col = p4est_quadrant_array_index (columns, zz);
+      P6EST_COLUMN_GET_RANGE (col, &first, &last);
+      for (zy = first; zy < last; zy++, quad_count++) {
+        layer = p2est_quadrant_array_index (layers, zy);
+        h2 = .5 * intsize * P4EST_QUADRANT_LEN (col->level);
+        h2z = .5 * intsize * P4EST_QUADRANT_LEN (layer->level);
+        k = 0;
+        for (zi = 0; zi < 2; ++zi) {
+          for (yi = 0; yi < 2; ++yi) {
+            for (xi = 0; xi < 2; ++xi) {
+              P4EST_ASSERT (0 <= k && k < P8EST_CHILDREN);
+              eta_x = intsize * col->x + h2 * (1. + (xi * 2 - 1) * scale);
+              eta_y = intsize * col->y + h2 * (1. + (yi * 2 - 1) * scale);
+              eta_z = intsize * layer->z + h2z * (1. + (zi * 2 - 1) * scale);
+              for (j = 0; j < 3; ++j) {
+                /* *INDENT-OFF* */
+                xyz[j] =
+                        ((1. - eta_z) * ((1. - eta_y) * ((1. - eta_x) * v[3 * 0 + j] +
+                                                         eta_x  * v[3 * 1 + j]) +
+                                         eta_y  * ((1. - eta_x) * v[3 * 2 + j] +
+                                                   eta_x  * v[3 * 3 + j]))
+                         +     eta_z  * ((1. - eta_y) * ((1. - eta_x) * v[3 * 4 + j] +
+                                                         eta_x  * v[3 * 5 + j]) +
+                                         eta_y  * ((1. - eta_x) * v[3 * 6 + j] +
+                                                   eta_x  * v[3 * 7 + j]))
+                        );
+                /* *INDENT-ON* */
+              }
+              for (j = 0; j < 3; ++j) {
+                float_data[3 * (P8EST_CHILDREN * quad_count + k) +
+                           j] = (P4EST_VTK_FLOAT_TYPE) xyz[j];
+              }
+              ++k;
+            }
+          }
+        }
+        P4EST_ASSERT (k == P8EST_CHILDREN);
+      }
+    }
+  }
+  P4EST_ASSERT (P8EST_CHILDREN * quad_count == Ntotal);
+
+#ifdef P4EST_VTK_ASCII
+  for (il = 0; il < Ntotal; ++il) {
+    wx = float_data[3 * il + 0];
+    wy = float_data[3 * il + 1];
+    wz = float_data[3 * il + 2];
+
+#ifdef P4EST_VTK_DOUBLES
+    fprintf (vtufile, "     %24.16e %24.16e %24.16e\n", wx, wy, wz);
+#else
+    fprintf (vtufile, "          %16.8e %16.8e %16.8e\n", wx, wy, wz);
+#endif
+  }
+#else
+  fprintf (vtufile, "          ");
+  /* TODO: Don't allocate the full size of the array, only allocate
+   * the chunk that will be passed to zlib and do this a chunk
+   * at a time.
+   */
+  retval = p6est_vtk_write_binary (vtufile, (char *) float_data,
+                                   sizeof (*float_data) * 3 * Ntotal);
+  fprintf (vtufile, "\n");
+  if (retval) {
+    P4EST_LERROR ("p6est_vtk: Error encoding points\n");
+    fclose (vtufile);
+    return -1;
+  }
+#endif
+  P4EST_FREE (float_data);
+  fprintf (vtufile, "        </DataArray>\n");
+  fprintf (vtufile, "      </Points>\n");
+  fprintf (vtufile, "      <Cells>\n");
+
+  /* write connectivity data */
+  fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"connectivity\""
+           " format=\"%s\">\n", P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+  for (sk = 0, il = 0; il < Ncells; ++il) {
+    fprintf (vtufile, "         ");
+    for (k = 0; k < P8EST_CHILDREN; ++sk, ++k) {
+      fprintf (vtufile, " %lld", (long long) sk);
+    }
+    fprintf (vtufile, "\n");
+  }
+#else
+  locidx_data = P4EST_ALLOC (p4est_locidx_t, Ncorners);
+  fprintf (vtufile, "          ");
+  for (il = 0; il < Ncorners; ++il) {
+    locidx_data[il] = il;
+  }
+  retval = p6est_vtk_write_binary (vtufile, (char *) locidx_data,
+                                   sizeof (*locidx_data) * Ncorners);
+  fprintf (vtufile, "\n");
+  if (retval) {
+    P4EST_LERROR ("p6est_vtk: Error encoding connectivity\n");
+    fclose (vtufile);
+    return -1;
+  }
+#endif
+  fprintf (vtufile, "        </DataArray>\n");
+
+  /* write offset data */
+  fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"offsets\""
+           " format=\"%s\">\n", P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+  fprintf (vtufile, "         ");
+  for (il = 1, sk = 1; il <= Ncells; ++il, ++sk) {
+    fprintf (vtufile, " %lld", (long long) (P8EST_CHILDREN * il));
+    if (!(sk % 8) && il != Ncells)
+      fprintf (vtufile, "\n         ");
+  }
+  fprintf (vtufile, "\n");
+#else
+  for (il = 1; il <= Ncells; ++il)
+    locidx_data[il - 1] = P8EST_CHILDREN * il;  /* same type */
+
+  fprintf (vtufile, "          ");
+  retval = p6est_vtk_write_binary (vtufile, (char *) locidx_data,
+                                   sizeof (*locidx_data) * Ncells);
+  fprintf (vtufile, "\n");
+  if (retval) {
+    P4EST_LERROR ("p6est_vtk: Error encoding offsets\n");
+    fclose (vtufile);
+    return -1;
+  }
+#endif
+  fprintf (vtufile, "        </DataArray>\n");
+
+  /* write type data */
+  fprintf (vtufile, "        <DataArray type=\"UInt8\" Name=\"types\""
+           " format=\"%s\">\n", P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+  fprintf (vtufile, "         ");
+  for (il = 0, sk = 1; il < Ncells; ++il, ++sk) {
+    fprintf (vtufile, " %d", P4EST_VTK_CELL_TYPE);
+    if (!(sk % 20) && il != (Ncells - 1))
+      fprintf (vtufile, "\n         ");
+  }
+  fprintf (vtufile, "\n");
+#else
+  uint8_data = P4EST_ALLOC (uint8_t, Ncells);
+  for (il = 0; il < Ncells; ++il)
+    uint8_data[il] = P4EST_VTK_CELL_TYPE;
+
+  fprintf (vtufile, "          ");
+  retval = p6est_vtk_write_binary (vtufile, (char *) uint8_data,
+                                   sizeof (*uint8_data) * Ncells);
+  P4EST_FREE (uint8_data);
+  fprintf (vtufile, "\n");
+  if (retval) {
+    P4EST_LERROR ("p6est_vtk: Error encoding types\n");
+    fclose (vtufile);
+    return -1;
+  }
+#endif
+  fprintf (vtufile, "        </DataArray>\n");
+  fprintf (vtufile, "      </Cells>\n");
+
+  if (write_rank || write_tree) {
+    fprintf (vtufile, "      <CellData Scalars=\"%s\">\n",
+             !write_tree ? "mpirank" : !write_rank ? "treeid" :
+             "mpirank,treeid");
+  }
+  if (write_rank) {
+    const int           wrapped_rank =
+      wrap_rank > 0 ? mpirank % wrap_rank : mpirank;
+
+    fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"mpirank\""
+             " format=\"%s\">\n", P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+    fprintf (vtufile, "         ");
+    for (il = 0, sk = 1; il < Ncells; ++il, ++sk) {
+      fprintf (vtufile, " %d", wrapped_rank);
+      if (!(sk % 20) && il != (Ncells - 1))
+        fprintf (vtufile, "\n         ");
+    }
+    fprintf (vtufile, "\n");
+#else
+    for (il = 0; il < Ncells; ++il)
+      locidx_data[il] = (p4est_locidx_t) wrapped_rank;
+
+    fprintf (vtufile, "          ");
+    retval = p6est_vtk_write_binary (vtufile, (char *) locidx_data,
+                                     sizeof (*locidx_data) * Ncells);
+    fprintf (vtufile, "\n");
+    if (retval) {
+      P4EST_LERROR ("p6est_vtk: Error encoding types\n");
+      fclose (vtufile);
+      return -1;
+    }
+#endif
+    fprintf (vtufile, "        </DataArray>\n");
+  }
+  if (write_tree) {
+    fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"treeid\""
+             " format=\"%s\">\n", P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+    fprintf (vtufile, "         ");
+    for (il = 0, sk = 1, jt = first_local_tree; jt <= last_local_tree; ++jt) {
+      tree = p4est_tree_array_index (trees, jt);
+      num_cols = tree->quadrants.elem_count;
+      columns = &tree->quadrants;
+      for (zz = 0; zz < num_cols; ++zz) {
+        col = p4est_quadrant_array_index (columns, zz);
+        P6EST_COLUMN_GET_RANGE (col, &first, &last);
+        for (zy = first; zy < last; zy++, sk++, il++) {
+          fprintf (vtufile, " %lld", (long long) jt);
+          if (!(sk % 20) && il != (Ncells - 1))
+            fprintf (vtufile, "\n         ");
+        }
+      }
+    }
+    fprintf (vtufile, "\n");
+#else
+    for (il = 0, jt = first_local_tree; jt <= last_local_tree; ++jt) {
+      tree = p4est_tree_array_index (trees, jt);
+      num_cols = tree->quadrants.elem_count;
+      columns = &tree->quadrants;
+      for (zz = 0; zz < num_cols; ++zz) {
+        col = p4est_quadrant_array_index (columns, zz);
+        P6EST_COLUMN_GET_RANGE (col, &first, &last);
+        for (zy = first; zy < last; zy++, il++) {
+          locidx_data[il] = (p4est_locidx_t) jt;
+        }
+      }
+    }
+    fprintf (vtufile, "          ");
+    retval = p6est_vtk_write_binary (vtufile, (char *) locidx_data,
+                                     sizeof (*locidx_data) * Ncells);
+    fprintf (vtufile, "\n");
+    if (retval) {
+      P4EST_LERROR ("p6est_vtk: Error encoding types\n");
+      fclose (vtufile);
+      return -1;
+    }
+#endif
+    fprintf (vtufile, "        </DataArray>\n");
+    P4EST_ASSERT (il == Ncells);
+  }
+  if (write_rank || write_tree) {
+    fprintf (vtufile, "      </CellData>\n");
+  }
+#ifndef P4EST_VTK_ASCII
+  P4EST_FREE (locidx_data);
+#endif
+
+  fprintf (vtufile, "      <PointData");
+  if (point_scalars != NULL)
+    fprintf (vtufile, " Scalars=\"%s\"", point_scalars);
+  if (point_vectors != NULL)
+    fprintf (vtufile, " Vectors=\"%s\"", point_vectors);
+  fprintf (vtufile, ">\n");
+
+  if (ferror (vtufile)) {
+    P4EST_LERROR ("p6est_vtk: Error writing header\n");
+    fclose (vtufile);
+    return -1;
+  }
+  if (fclose (vtufile)) {
+    P4EST_LERROR ("p6est_vtk: Error closing header\n");
+    return -1;
+  }
+  vtufile = NULL;
+
+  /* Only have the root write to the parallel vtk file */
+  if (mpirank == 0) {
+    char                pvtufilename[BUFSIZ];
+    FILE               *pvtufile;
+
+    snprintf (pvtufilename, BUFSIZ, "%s.pvtu", filename);
+
+    pvtufile = fopen (pvtufilename, "wb");
+    if (!pvtufile) {
+      P4EST_LERRORF ("Could not open %s for output\n", vtufilename);
+      return -1;
+    }
+
+    fprintf (pvtufile, "<?xml version=\"1.0\"?>\n");
+    fprintf (pvtufile, "<VTKFile type=\"PUnstructuredGrid\" version=\"0.1\"");
+#if defined P4EST_VTK_BINARY && defined P4EST_VTK_COMPRESSION
+    fprintf (pvtufile, " compressor=\"vtkZLibDataCompressor\"");
+#endif
+#ifdef SC_IS_BIGENDIAN
+    fprintf (pvtufile, " byte_order=\"BigEndian\">\n");
+#else
+    fprintf (pvtufile, " byte_order=\"LittleEndian\">\n");
+#endif
+
+    fprintf (pvtufile, "  <PUnstructuredGrid GhostLevel=\"0\">\n");
+    fprintf (pvtufile, "    <PPoints>\n");
+    fprintf (pvtufile, "      <PDataArray type=\"%s\" Name=\"Position\""
+             " NumberOfComponents=\"3\" format=\"%s\"/>\n",
+             P4EST_VTK_FLOAT_NAME, P4EST_VTK_FORMAT_STRING);
+    fprintf (pvtufile, "    </PPoints>\n");
+    if (write_rank || write_tree) {
+      fprintf (pvtufile, "    <PCellData Scalars=\"%s\">\n",
+               !write_tree ? "mpirank" : !write_rank ? "treeid" :
+               "mpirank,treeid");
+    }
+    if (write_rank) {
+      fprintf (pvtufile, "      "
+               "<PDataArray type=\"%s\" Name=\"mpirank\" format=\"%s\"/>\n",
+               P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+    }
+    if (write_tree) {
+      fprintf (pvtufile, "      "
+               "<PDataArray type=\"%s\" Name=\"treeid\" format=\"%s\"/>\n",
+               P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+    }
+    if (write_rank || write_tree) {
+      fprintf (pvtufile, "    </PCellData>\n");
+    }
+    fprintf (pvtufile, "    <PPointData>\n");
+
+    if (ferror (pvtufile)) {
+      P4EST_LERROR ("p6est_vtk: Error writing parallel header\n");
+      fclose (pvtufile);
+      return -1;
+    }
+    if (fclose (pvtufile)) {
+      P4EST_LERROR ("p6est_vtk: Error closing parallel header\n");
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+int
+p6est_vtk_write_point_scalar (p6est_t * p6est,
+                              const char *filename,
+                              const char *scalar_name, const double *values)
+{
+  const int           mpirank = p6est->mpirank;
+  const p4est_locidx_t Ncells = (p4est_locidx_t) p6est->layers->elem_count;
+  const p4est_locidx_t Ncorners = P8EST_CHILDREN * Ncells;      /* type ok */
+  int                 retval;
+  p4est_locidx_t      il;
+#ifndef P4EST_VTK_ASCII
+  P4EST_VTK_FLOAT_TYPE *float_data;
+#endif
+  char                vtufilename[BUFSIZ];
+  FILE               *vtufile;
+
+  /* Have each proc write to its own file */
+  snprintf (vtufilename, BUFSIZ, "%s_%04d.vtu", filename, mpirank);
+  /* To be able to fseek in a file you cannot open in append mode.
+   * so you need to open with "r+" and fseek to SEEK_END.
+   */
+  vtufile = fopen (vtufilename, "rb+");
+  if (vtufile == NULL) {
+    P4EST_LERRORF ("Could not open %s for output\n", vtufilename);
+    return -1;
+  }
+  retval = fseek (vtufile, 0L, SEEK_END);
+  if (retval) {
+    P4EST_LERRORF ("Could not fseek %s for output\n", vtufilename);
+    fclose (vtufile);
+    return -1;
+  }
+
+  /* write point position data */
+  fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"%s\""
+           " format=\"%s\">\n",
+           P4EST_VTK_FLOAT_NAME, scalar_name, P4EST_VTK_FORMAT_STRING);
+
+#ifdef P4EST_VTK_ASCII
+  for (il = 0; il < Ncorners; ++il) {
+#ifdef P4EST_VTK_DOUBLES
+    fprintf (vtufile, "     %24.16e\n", values[il]);
+#else
+    fprintf (vtufile, "          %16.8e\n", values[il]);
+#endif
+  }
+#else
+  float_data = P4EST_ALLOC (P4EST_VTK_FLOAT_TYPE, Ncorners);
+  for (il = 0; il < Ncorners; ++il) {
+    float_data[il] = (P4EST_VTK_FLOAT_TYPE) values[il];
+  }
+
+  fprintf (vtufile, "          ");
+  /* TODO: Don't allocate the full size of the array, only allocate
+   * the chunk that will be passed to zlib and do this a chunk
+   * at a time.
+   */
+  retval = p6est_vtk_write_binary (vtufile, (char *) float_data,
+                                   sizeof (*float_data) * Ncorners);
+  fprintf (vtufile, "\n");
+  if (retval) {
+    P4EST_LERROR ("p6est_vtk: Error encoding points\n");
+    fclose (vtufile);
+    return -1;
+  }
+  P4EST_FREE (float_data);
+#endif
+  fprintf (vtufile, "        </DataArray>\n");
+
+  if (ferror (vtufile)) {
+    P4EST_LERROR ("p6est_vtk: Error writing point scalar\n");
+    fclose (vtufile);
+    return -1;
+  }
+  if (fclose (vtufile)) {
+    P4EST_LERROR ("p6est_vtk: Error closing point scalar\n");
+    return -1;
+  }
+  vtufile = NULL;
+
+  /* Only have the root write to the parallel vtk file */
+  if (mpirank == 0) {
+    char                pvtufilename[BUFSIZ];
+    FILE               *pvtufile;
+    snprintf (pvtufilename, BUFSIZ, "%s.pvtu", filename);
+
+    pvtufile = fopen (pvtufilename, "ab");
+    if (!pvtufile) {
+      P4EST_LERRORF ("Could not open %s for output\n", vtufilename);
+      return -1;
+    }
+
+    fprintf (pvtufile, "      <PDataArray type=\"%s\" Name=\"%s\""
+             " format=\"%s\"/>\n",
+             P4EST_VTK_FLOAT_NAME, scalar_name, P4EST_VTK_FORMAT_STRING);
+
+    if (ferror (pvtufile)) {
+      P4EST_LERROR ("p6est_vtk: Error writing parallel point scalar\n");
+      fclose (pvtufile);
+      return -1;
+    }
+    if (fclose (pvtufile)) {
+      P4EST_LERROR ("p6est_vtk: Error closing parallel point scalar\n");
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+int
+p6est_vtk_write_point_vector (p6est_t * p6est,
+                              const char *filename,
+                              const char *vector_name, const double *values)
+{
+  SC_ABORT ("p6est_vtk_write_point_vector not implemented");
+}
+
+int
+p6est_vtk_write_footer (p6est_t * p4est, const char *filename)
+{
+  char                vtufilename[BUFSIZ];
+  int                 p;
+  int                 procRank = p4est->mpirank;
+  int                 numProcs = p4est->mpisize;
+  FILE               *vtufile;
+
+  /* Have each proc write to its own file */
+  snprintf (vtufilename, BUFSIZ, "%s_%04d.vtu", filename, procRank);
+  vtufile = fopen (vtufilename, "ab");
+  if (vtufile == NULL) {
+    P4EST_LERRORF ("Could not open %s for output!\n", vtufilename);
+    return -1;
+  }
+
+  fprintf (vtufile, "      </PointData>\n");
+  fprintf (vtufile, "    </Piece>\n");
+  fprintf (vtufile, "  </UnstructuredGrid>\n");
+  fprintf (vtufile, "</VTKFile>\n");
+
+  if (ferror (vtufile)) {
+    P4EST_LERROR ("p4est_vtk: Error writing footer\n");
+    fclose (vtufile);
+    return -1;
+  }
+  if (fclose (vtufile)) {
+    P4EST_LERROR ("p4est_vtk: Error closing footer\n");
+    return -1;
+  }
+  vtufile = NULL;
+
+  /* Only have the root write to the parallel vtk file */
+  if (procRank == 0) {
+    char                visitfilename[BUFSIZ];
+    char                pvtufilename[BUFSIZ];
+    FILE               *pvtufile, *visitfile;
+
+    /* Reopen paraview master file for writing bottom half */
+    snprintf (pvtufilename, BUFSIZ, "%s.pvtu", filename);
+    pvtufile = fopen (pvtufilename, "ab");
+    if (!pvtufile) {
+      P4EST_LERRORF ("Could not open %s for output!\n", vtufilename);
+      return -1;
+    }
+
+    /* Create a master file for visualization in Visit */
+    snprintf (visitfilename, BUFSIZ, "%s.visit", filename);
+    visitfile = fopen (visitfilename, "wb");
+    if (!visitfile) {
+      P4EST_LERRORF ("Could not open %s for output\n", visitfilename);
+      fclose (pvtufile);
+      return -1;
+    }
+    fprintf (visitfile, "!NBLOCKS %d\n", numProcs);
+
+    /* Write data about the parallel pieces into both files */
+    fprintf (pvtufile, "    </PPointData>\n");
+    for (p = 0; p < numProcs; ++p) {
+      fprintf (pvtufile,
+               "    <Piece Source=\"%s_%04d.vtu\"/>\n", filename, p);
+      fprintf (visitfile, "%s_%04d.vtu\n", filename, p);
+    }
+    fprintf (pvtufile, "  </PUnstructuredGrid>\n");
+    fprintf (pvtufile, "</VTKFile>\n");
+
+    /* Close paraview master file */
+    if (ferror (pvtufile)) {
+      P4EST_LERROR ("p4est_vtk: Error writing parallel footer\n");
+      fclose (visitfile);
+      fclose (pvtufile);
+      return -1;
+    }
+    if (fclose (pvtufile)) {
+      fclose (visitfile);
+      P4EST_LERROR ("p4est_vtk: Error closing parallel footer\n");
+      return -1;
+    }
+
+    /* Close visit master file */
+    if (ferror (visitfile)) {
+      P4EST_LERROR ("p4est_vtk: Error writing parallel footer\n");
+      fclose (visitfile);
+      return -1;
+    }
+    if (fclose (visitfile)) {
+      P4EST_LERROR ("p4est_vtk: Error closing parallel footer\n");
+      return -1;
+    }
+  }
+
+  return 0;
+}
diff --git a/example/p6est/p6est_vtk.h b/example/p6est/p6est_vtk.h
new file mode 100644
index 0000000..234759a
--- /dev/null
+++ b/example/p6est/p6est_vtk.h
@@ -0,0 +1,173 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2013 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P6EST_VTK_H
+#define P6EST_VTK_H
+
+#include <p6est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** This writes out the p6est in VTK format.
+ *
+ * This is a convenience function for the special
+ * case of writing out the tree id and MPI rank only.
+ * One file is written per MPI rank, and one meta file on rank 0.
+ * This function will abort if there is a file error.
+ *
+ * \param [in] p6est    The p6est to be written.
+ * \param [in] filename The first part of the file name which will have the
+ *                      MPI rank appended to it: The output file will be
+ *                      filename_rank.vtu, and the meta file filename.pvtu).
+ */
+void                p6est_vtk_write_file (p6est_t * p6est,
+                                          const char *filename);
+
+/** This writes out the p6est and any number of point fields in VTK format.
+ *
+ * This is a convenience function that will abort if there is a file error.
+ *
+ * \param [in] p6est    The p6est to be written.
+ * \param [in] scale    Double value between 0 and 1 to scale each quadrant.
+ * \param [in] write_tree   Include the tree id as output field.
+ * \param [in] write_rank   Include the MPI rank as output field.
+ * \param [in] wrap_tree    The MPI rank is written module wrap_tree, or 0.
+ * \param filename      First part of the name, see p6est_vtk_write_file.
+ * \param num_scalars   Number of scalar fields to write.
+ * \param num_vectors   Number of vector fields to write.
+ *
+ * The variable arguments need to be pairs of (fieldname, fieldvalues)
+ * where the scalars come first, then the vectors.
+ */
+void                p6est_vtk_write_all (p6est_t * p6est,
+                                         double scale, int write_tree,
+                                         int write_rank, int wrap_rank,
+                                         int num_scalars, int num_vectors,
+                                         const char *filename, ...);
+
+/** This will write the header of the vtu file.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of
+ * fields.  The calling sequence would be something like
+ *
+ * \begincode
+ * p6est_vtk_write_header(p6est, 1., 1, 1, 0, "output");
+ * p6est_vtk_write_point_scalar (...);
+ * ...
+ * p6est_vtk_write_footer(p6est, "output");
+ * \endcode
+ *
+ * \param p6est     The p6est to be written.
+ * \param scale     The relative length factor of the quadrants.
+ *                  Use 1.0 to fit quadrants exactly, less to create gaps.
+ * \param write_tree    Boolean to determine if the tree id should be output.
+ * \param write_rank    Boolean to determine if the MPI rank should be output.
+ * \param wrap_rank Number to wrap around the rank with a modulo operation.
+ *                  Can be 0 for no wrapping.
+ * \param point_scalars  Comma-separated list of point scalar fields, or NULL.
+ * \param point_vectors  Comma-separated list of point vector fields, or NULL.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p6est_vtk_write_header (p6est_t * p6est,
+                                            double scale, int write_tree,
+                                            int write_rank, int wrap_rank,
+                                            const char *point_scalars,
+                                            const char *point_vectors,
+                                            const char *filename);
+
+/** This will write a scalar field to the vtu file.
+ *
+ * It is good practice to make sure that the scalar field also
+ * exists in the comma separated string \a point_scalars passed
+ * to \c p4est_vtk_write_header.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of fields.
+ *
+ * \param p6est     The p6est to be written.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ * \param scalar_name The name of the scalar field.
+ * \param values    The point values that will be written.
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p6est_vtk_write_point_scalar (p6est_t * p6est,
+                                                  const char *filename,
+                                                  const char *scalar_name,
+                                                  const double *values);
+
+/** This will write a 3-vector field to the vtu file.
+ *
+ * It is good practice to make sure that the vector field also
+ * exists in the comma separated string \a point_vectors passed
+ * to \c p6est_vtk_write_header.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of fields.
+ *
+ * \param p6est     The p6est to be written.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ * \param vector_name The name of the vector field.
+ * \param values    The point values that will be written.
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p6est_vtk_write_point_vector (p6est_t * p6est,
+                                                  const char *filename,
+                                                  const char *vector_name,
+                                                  const double *values);
+
+/** This will write the footer of the vtu file.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of
+ * fields.  To write out two fields the
+ * calling sequence would be something like
+ *
+ * \begincode
+ * p6est_vtk_write_header(p6est, ..., "output");
+ * p6est_vtk_write_footer(p6est, "output");
+ * \endcode
+ *
+ * \param p6est     The p6est to be written.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p6est_vtk_write_footer (p6est_t * p6est,
+                                            const char *filename);
+
+SC_EXTERN_C_END;
+
+#endif /* P6EST_VTK_H */
diff --git a/example/p6est/test/test_all.c b/example/p6est/test/test_all.c
new file mode 100644
index 0000000..f002c73
--- /dev/null
+++ b/example/p6est/test/test_all.c
@@ -0,0 +1,422 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2014 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_bits.h>
+#include <p6est.h>
+#include <p6est_extended.h>
+#include <p6est_ghost.h>
+#include <p6est_vtk.h>
+#include <p6est_lnodes.h>
+#include <sc_flops.h>
+#include <sc_statistics.h>
+#include <sc_options.h>
+
+char                test_data = 'x';
+char               *TEST_USER_POINTER = &test_data;
+
+static int          refine_level = -1;
+static int          refine_zlevel = -1;
+
+/* To define a p6est_refine_column_t, all we have to do is take a p4est refine
+ * function ... */
+static int
+p4est_refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+                 p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  if (quadrant->level >= refine_level) {
+    return 0;
+  }
+  if (which_tree == 2 || which_tree == 3) {
+    return 0;
+  }
+
+  cid = p4est_quadrant_child_id (quadrant);
+
+  if (cid == P4EST_CHILDREN - 1 ||
+      (quadrant->x >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2) &&
+       quadrant->y >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#ifdef P4_TO_P8
+       && quadrant->z >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#endif
+      )) {
+    return 1;
+  }
+  if (quadrant->level == 1 && cid == 2) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_QUADRANT_LEN (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->y >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+/* and wrap it.*/
+static int
+refine_column_fn (p6est_t * p6est, p4est_topidx_t which_tree,
+                  p4est_quadrant_t * column)
+{
+  return p4est_refine_fn (p6est->columns, which_tree, column);
+}
+
+static int
+refine_layer_fn (p6est_t * p6est, p4est_topidx_t which_tree,
+                 p4est_quadrant_t * column, p2est_quadrant_t * layer)
+{
+  p4est_topidx_t      tohash[4];
+  unsigned            hash;
+
+  tohash[0] = (p4est_topidx_t) column->x;
+  tohash[1] = (p4est_topidx_t) column->y;
+  tohash[2] = (p4est_topidx_t) layer->z;
+  tohash[3] = (((p4est_topidx_t) column->level) << 16) |
+    ((p4est_topidx_t) layer->level);
+
+  hash = p4est_topidx_hash4 (tohash);
+
+  return (layer->level < refine_zlevel && !((int) hash % 3));
+}
+
+void
+init_fn (p6est_t * p6est, p4est_topidx_t which_tree,
+         p4est_quadrant_t * col, p2est_quadrant_t * layer)
+{
+  SC_CHECK_ABORT (p6est->user_pointer == TEST_USER_POINTER,
+                  "user_pointer corruption\n");
+}
+
+static int
+coarsen_column_fn (p6est_t * p6est, p4est_topidx_t which_tree,
+                   p4est_quadrant_t * column[])
+{
+  return 1;
+}
+
+static int
+weight_fn (p6est_t * p6est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * col, p2est_quadrant_t * layer)
+{
+  return 1;
+}
+
+static int
+coarsen_layer_fn (p6est_t * p6est, p4est_topidx_t which_tree,
+                  p4est_quadrant_t * column, p2est_quadrant_t * layers[])
+{
+  return 1;
+}
+
+enum
+{
+  TIMINGS_CONNECTIVITY,
+  TIMINGS_NEW,
+  TIMINGS_NEW_EXT,
+  TIMINGS_REFINE_COLUMNS_A,
+  TIMINGS_REFINE_COLUMNS_B,
+  TIMINGS_REFINE_LAYERS,
+  TIMINGS_COARSEN_COLUMNS,
+  TIMINGS_COARSEN_LAYERS,
+  TIMINGS_GHOST_FACE,
+  TIMINGS_GHOST_FULL,
+  TIMINGS_GHOST_EXPAND_1,
+  TIMINGS_GHOST_EXPAND_2,
+  TIMINGS_BALANCE_FACE,
+  TIMINGS_BALANCE_EDGE,
+  TIMINGS_BALANCE_FULL,
+  TIMINGS_PARTITION,
+  TIMINGS_PARTITION_SAME,
+  TIMINGS_LNODES_1,
+  TIMINGS_LNODES_2,
+  TIMINGS_LNODES_3,
+  TIMINGS_SAVE,
+  TIMINGS_LOAD,
+  TIMINGS_NUM_STATS
+};
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm = sc_MPI_COMM_WORLD;
+  p4est_connectivity_t *conn4;
+  p6est_connectivity_t *conn, *copy_conn;
+  p6est_t            *p6est, *copy_p6est;
+  p6est_ghost_t      *ghost;
+  double              height[3] = { 0., 0., 0.1 };
+  int                 i;
+  int                 vtk;
+  unsigned            crc_computed;
+  sc_options_t       *opt;
+  int                 first_argc;
+  const char         *config_name;
+  const char         *save_filename = NULL;
+  sc_statinfo_t       stats[TIMINGS_NUM_STATS];
+  sc_flopinfo_t       fi, snapshot;
+  int                 mpiret;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+#ifndef P4EST_DEBUG
+  sc_set_log_defaults (NULL, NULL, SC_LP_STATISTICS);
+#endif
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  opt = sc_options_new (argv[0]);
+
+  sc_options_add_int (opt, 'l', "level", &refine_level, 1,
+                      "initial refine level");
+  sc_options_add_int (opt, 'z', "z-level", &refine_zlevel, 2,
+                      "initial refine level");
+  sc_options_add_string (opt, 'c', "configuration", &config_name, "unit",
+                         "configuration: brick23|corner|cubed|disk|moebius|periodic|pillow|rotwrap|star|unit");
+  sc_options_add_string (opt, 'P', "save-file", &save_filename,
+                         NULL, "filename for saving");
+  sc_options_add_switch (opt, 'w', "write-vtk", &vtk, "write vtk files");
+
+  first_argc = sc_options_parse (p4est_package_id, SC_LP_DEFAULT,
+                                 opt, argc, argv);
+
+  if (first_argc < 0 || first_argc != argc) {
+    sc_options_print_usage (p4est_package_id, SC_LP_ERROR, opt, NULL);
+    return 1;
+  }
+  sc_options_print_summary (p4est_package_id, SC_LP_PRODUCTION, opt);
+
+  /* start overall timing */
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  sc_flops_start (&fi);
+
+  conn4 = p4est_connectivity_new_byname (config_name);
+  SC_CHECK_ABORTF (conn4 != NULL, "Invalid connectivity name: %s\n",
+                   config_name);
+
+  sc_flops_snap (&fi, &snapshot);
+  conn = p6est_connectivity_new (conn4, NULL, height);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_CONNECTIVITY], snapshot.iwtime,
+                 "Connectivity");
+
+  p4est_connectivity_destroy (conn4);
+
+  sc_flops_snap (&fi, &snapshot);
+  p6est = p6est_new (mpicomm, conn, 4, init_fn, TEST_USER_POINTER);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_NEW], snapshot.iwtime, "New");
+  p6est_destroy (p6est);
+
+  sc_flops_snap (&fi, &snapshot);
+  p6est = p6est_new_ext (mpicomm, conn, 0, refine_level, refine_zlevel, 1, 3,
+                         init_fn, TEST_USER_POINTER);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_NEW_EXT], snapshot.iwtime, "New extended");
+
+  refine_level += 2;
+  sc_flops_snap (&fi, &snapshot);
+  p6est_refine_columns (p6est, 1, refine_column_fn, init_fn);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_REFINE_COLUMNS_A], snapshot.iwtime,
+                 "Refine columns A");
+
+  refine_zlevel += 2;
+  sc_flops_snap (&fi, &snapshot);
+  p6est_refine_layers (p6est, 1, refine_layer_fn, init_fn);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_REFINE_LAYERS], snapshot.iwtime,
+                 "Refine layers");
+
+  refine_level += 2;
+  sc_flops_snap (&fi, &snapshot);
+  p6est_refine_columns (p6est, 1, refine_column_fn, init_fn);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_REFINE_COLUMNS_B], snapshot.iwtime,
+                 "Refine layers B");
+
+  copy_p6est = p6est_copy (p6est, 1);
+  sc_flops_snap (&fi, &snapshot);
+  p6est_coarsen_columns (copy_p6est, 1, coarsen_column_fn, init_fn);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_COARSEN_COLUMNS], snapshot.iwtime,
+                 "Coarsen columns");
+  if (vtk) {
+    p6est_vtk_write_file (copy_p6est, "p6est_test_coarsen_columns");
+  }
+  p6est_destroy (copy_p6est);
+
+  copy_p6est = p6est_copy (p6est, 1);
+  sc_flops_snap (&fi, &snapshot);
+  p6est_coarsen_layers (copy_p6est, 0, coarsen_layer_fn, init_fn);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_COARSEN_LAYERS], snapshot.iwtime,
+                 "Coarsen layers");
+  if (vtk) {
+    p6est_vtk_write_file (copy_p6est, "p6est_test_coarsen_layers");
+  }
+  p6est_destroy (copy_p6est);
+
+  if (vtk) {
+    p6est_vtk_write_file (p6est, "p6est_test_pre_balance");
+  }
+
+  sc_flops_snap (&fi, &snapshot);
+  ghost = p6est_ghost_new (p6est, P4EST_CONNECT_FACE);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_GHOST_FACE], snapshot.iwtime, "Ghost face");
+  p6est_ghost_destroy (ghost);
+
+  sc_flops_snap (&fi, &snapshot);
+  ghost = p6est_ghost_new (p6est, P4EST_CONNECT_FULL);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_GHOST_FULL], snapshot.iwtime, "Ghost full");
+
+  sc_flops_snap (&fi, &snapshot);
+  p6est_ghost_expand (p6est, ghost);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_GHOST_EXPAND_1], snapshot.iwtime,
+                 "Ghost expand 1");
+
+  sc_flops_snap (&fi, &snapshot);
+  p6est_ghost_expand (p6est, ghost);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_GHOST_EXPAND_2], snapshot.iwtime,
+                 "Ghost expand 2");
+
+  p6est_ghost_destroy (ghost);
+
+  sc_flops_snap (&fi, &snapshot);
+  p6est_balance (p6est, P8EST_CONNECT_FACE, init_fn);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_FACE], snapshot.iwtime,
+                 "Balance face");
+
+  if (vtk) {
+    p6est_vtk_write_file (p6est, "p6est_test_balance_face");
+  }
+
+  sc_flops_snap (&fi, &snapshot);
+  p6est_balance (p6est, P8EST_CONNECT_EDGE, init_fn);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_EDGE], snapshot.iwtime,
+                 "Balance edge");
+
+  if (vtk) {
+    p6est_vtk_write_file (p6est, "p6est_test_balance_edge");
+  }
+
+  sc_flops_snap (&fi, &snapshot);
+  p6est_balance (p6est, P8EST_CONNECT_FULL, init_fn);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_FULL], snapshot.iwtime,
+                 "Balance full");
+
+  if (vtk) {
+    p6est_vtk_write_file (p6est, "p6est_test_balance_full");
+  }
+
+  sc_flops_snap (&fi, &snapshot);
+  p6est_partition (p6est, weight_fn);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_PARTITION], snapshot.iwtime, "Partition");
+
+  sc_flops_snap (&fi, &snapshot);
+  p6est_partition (p6est, NULL);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_PARTITION_SAME], snapshot.iwtime,
+                 "Partition same");
+
+  if (vtk) {
+    p6est_vtk_write_file (p6est, "p6est_test_partition");
+  }
+
+  for (i = 1; i <= 3; i++) {
+    p6est_lnodes_t     *lnodes;
+
+    sc_flops_snap (&fi, &snapshot);
+    lnodes = p6est_lnodes_new (p6est, NULL, i);
+    sc_flops_shot (&fi, &snapshot);
+    switch (i) {
+    case 1:
+      sc_stats_set1 (&stats[TIMINGS_LNODES_1], snapshot.iwtime, "Lnodes 1");
+      break;
+    case 2:
+      sc_stats_set1 (&stats[TIMINGS_LNODES_2], snapshot.iwtime, "Lnodes 2");
+      break;
+    case 3:
+      sc_stats_set1 (&stats[TIMINGS_LNODES_3], snapshot.iwtime, "Lnodes 3");
+      break;
+    }
+
+    p6est_lnodes_destroy (lnodes);
+  }
+
+  crc_computed = p6est_checksum (p6est);
+
+  P4EST_GLOBAL_PRODUCTIONF ("p6est checksum 0x%08x\n", crc_computed);
+
+  if (save_filename) {
+    sc_flops_snap (&fi, &snapshot);
+    p6est_save (save_filename, p6est, 1);
+    sc_flops_shot (&fi, &snapshot);
+    sc_stats_set1 (&stats[TIMINGS_SAVE], snapshot.iwtime, "Save");
+
+    sc_flops_snap (&fi, &snapshot);
+    copy_p6est = p6est_load (save_filename, p6est->mpicomm,
+                             p6est->data_size, 1, p6est->user_pointer,
+                             &copy_conn);
+    sc_flops_shot (&fi, &snapshot);
+    sc_stats_set1 (&stats[TIMINGS_LOAD], snapshot.iwtime, "Load");
+
+    p6est_destroy (copy_p6est);
+
+    p6est_connectivity_destroy (copy_conn);
+  }
+  else {
+    sc_stats_set1 (&stats[TIMINGS_SAVE], 0., "Save");
+    sc_stats_set1 (&stats[TIMINGS_LOAD], 0., "Load");
+  }
+
+  p6est_destroy (p6est);
+
+  p6est_connectivity_destroy (conn);
+
+  /* calculate and print timings */
+  sc_stats_compute (mpicomm, TIMINGS_NUM_STATS, stats);
+  sc_stats_print (p4est_package_id, SC_LP_STATISTICS,
+                  TIMINGS_NUM_STATS, stats, 1, 1);
+
+  sc_options_destroy (opt);
+
+  /* exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/points/Makefile.am b/example/points/Makefile.am
new file mode 100644
index 0000000..06a1dcc
--- /dev/null
+++ b/example/points/Makefile.am
@@ -0,0 +1,18 @@
+
+# This file is part of p4est.
+# Makefile.am in example/points
+# included non-recursively from toplevel directory
+
+if P4EST_ENABLE_BUILD_2D
+bin_PROGRAMS += example/points/p4est_points
+example_points_p4est_points_SOURCES = example/points/points2.c
+
+LINT_CSOURCES += $(example_points_p4est_points_SOURCES)
+endif
+
+if P4EST_ENABLE_BUILD_3D
+bin_PROGRAMS += example/points/p8est_points
+example_points_p8est_points_SOURCES = example/points/points3.c
+
+LINT_CSOURCES += $(example_points_p8est_points_SOURCES)
+endif
diff --git a/example/points/points2.c b/example/points/points2.c
new file mode 100644
index 0000000..5d3a4e4
--- /dev/null
+++ b/example/points/points2.c
@@ -0,0 +1,241 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/********************************************************************
+ *                          IMPORTANT NOTE                          *
+ *                                                                  *
+ * The p4est_points functionality depends on sc/src/sc_sort.        *
+ * That parallel bitonic sort is still buggy (see sc/bugs).         *
+ * If you want to use this code you have to fix the sort first.     *
+ ********************************************************************/
+
+#ifdef P4_TO_P8
+#include <p8est_bits.h>
+#include <p8est_points.h>
+#include <p8est_vtk.h>
+#else
+#include <p4est_bits.h>
+#include <p4est_points.h>
+#include <p4est_vtk.h>
+#endif /* !P4_TO_P8 */
+#include <sc_io.h>
+
+/*
+ * Usage: p4est_points <configuration> <level> <prefix>
+ *        possible configurations:
+ *        o unit      Refinement on the unit square.
+ *        o three     Refinement on a forest with three trees.
+ *        o moebius   Refinement on a 5-tree Moebius band.
+ *        o star      Refinement on a 6-tree star shaped domain.
+ *        o periodic  Refinement on the unit square with periodic b.c.
+ */
+
+static p4est_quadrant_t *
+read_points (const char *filename, p4est_topidx_t num_trees,
+             p4est_locidx_t * num_points)
+{
+  int                 retval;
+  int                 qshift;
+  unsigned            ucount, u, ignored;
+  double              x, y, z;
+  double              qlen;
+  double             *point_buffer;
+  p4est_quadrant_t   *points, *q;
+  FILE               *file;
+
+  file = fopen (filename, "rb");
+  SC_CHECK_ABORTF (file != NULL, "Open file %s", filename);
+
+  sc_fread (&ucount, sizeof (unsigned int), 1, file, "Read point count");
+
+  point_buffer = P4EST_ALLOC (double, 3 * ucount);
+  sc_fread (point_buffer, sizeof (double), (size_t) (3 * ucount), file,
+            "Read points");
+
+  retval = fclose (file);
+  SC_CHECK_ABORTF (retval == 0, "Close file %s", filename);
+
+  q = points = P4EST_ALLOC_ZERO (p4est_quadrant_t, ucount);
+  qlen = (double) (1 << P4EST_QMAXLEVEL);
+  qshift = P4EST_MAXLEVEL - P4EST_QMAXLEVEL;
+  for (ignored = u = 0; u < ucount; ++u) {
+    x = point_buffer[3 * u + 0];
+    y = point_buffer[3 * u + 1];
+    z = point_buffer[3 * u + 2];
+    if (x < 0. || x > 1. || y < 0. || y > 1. || z < 0. || z > 1.) {
+      ++ignored;
+      continue;
+    }
+
+    q->x = (p4est_qcoord_t) (x * qlen) << qshift;
+    q->x = SC_MIN (q->x, P4EST_ROOT_LEN - 1);
+    q->y = (p4est_qcoord_t) (y * qlen) << qshift;
+    q->y = SC_MIN (q->y, P4EST_ROOT_LEN - 1);
+#ifdef P4_TO_P8
+    q->z = (p4est_qcoord_t) (z * qlen) << qshift;
+    q->z = SC_MIN (q->z, P4EST_ROOT_LEN - 1);
+#endif
+    q->level = P4EST_MAXLEVEL;
+    q->p.which_tree =
+      (p4est_topidx_t) ((double) num_trees * rand () / (RAND_MAX + 1.0));
+    P4EST_ASSERT (p4est_quadrant_is_node (q, 1));
+
+    ++q;
+  }
+  P4EST_FREE (point_buffer);
+
+  if (num_points != NULL) {
+    *num_points = (p4est_locidx_t) (ucount - ignored);
+  }
+
+  return points;
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 num_procs, rank;
+  int                 maxlevel;
+  int                 wrongusage;
+  char                buffer[BUFSIZ];
+  p4est_locidx_t      num_points, max_points;
+  p4est_connectivity_t *conn;
+  p4est_quadrant_t   *points;
+  p4est_t            *p4est;
+  sc_MPI_Comm         mpicomm;
+  const char         *usage;
+
+  /* initialize MPI and p4est internals */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* process command line arguments */
+  usage =
+    "Arguments: <configuration> <level> <maxpoints> <prefix>\n"
+    "   Configuration can be any of\n"
+#ifndef P4_TO_P8
+    "      unit|three|moebius|star|periodic\n"
+#else
+    "      unit|periodic|rotwrap|twocubes|rotcubes\n"
+#endif
+    "   Level controls the maximum depth of refinement\n"
+    "   Maxpoints is the maximum number of points per quadrant\n"
+    "      which applies to all quadrants above maxlevel\n"
+    "      A value of 0 refines recursively to maxlevel\n"
+    "      A value of -1 does no refinement at all\n"
+    "   Prefix is for loading a point data file\n";
+  wrongusage = 0;
+  if (!wrongusage && argc != 5) {
+    wrongusage = 1;
+  }
+  conn = NULL;
+  if (!wrongusage) {
+#ifndef P4_TO_P8
+    if (!strcmp (argv[1], "unit")) {
+      conn = p4est_connectivity_new_unitsquare ();
+    }
+    else if (!strcmp (argv[1], "three")) {
+      conn = p4est_connectivity_new_corner ();
+    }
+    else if (!strcmp (argv[1], "moebius")) {
+      conn = p4est_connectivity_new_moebius ();
+    }
+    else if (!strcmp (argv[1], "star")) {
+      conn = p4est_connectivity_new_star ();
+    }
+    else if (!strcmp (argv[1], "periodic")) {
+      conn = p4est_connectivity_new_periodic ();
+    }
+#else
+    if (!strcmp (argv[1], "unit")) {
+      conn = p8est_connectivity_new_unitcube ();
+    }
+    else if (!strcmp (argv[1], "periodic")) {
+      conn = p8est_connectivity_new_periodic ();
+    }
+    else if (!strcmp (argv[1], "rotwrap")) {
+      conn = p8est_connectivity_new_rotwrap ();
+    }
+    else if (!strcmp (argv[1], "twocubes")) {
+      conn = p8est_connectivity_new_twocubes ();
+    }
+    else if (!strcmp (argv[1], "rotcubes")) {
+      conn = p8est_connectivity_new_rotcubes ();
+    }
+#endif
+    else {
+      wrongusage = 1;
+    }
+  }
+  if (!wrongusage) {
+    maxlevel = atoi (argv[2]);
+    if (maxlevel < 0 || maxlevel > P4EST_QMAXLEVEL) {
+      wrongusage = 1;
+    }
+  }
+  if (!wrongusage) {
+    max_points = (p4est_locidx_t) atoi (argv[3]);
+    if (max_points < -1) {
+      wrongusage = 1;
+    }
+  }
+  if (wrongusage) {
+    P4EST_GLOBAL_LERROR (usage);
+    sc_abort_collective ("Usage error");
+  }
+
+  snprintf (buffer, BUFSIZ, "%s%d_%d.pts", argv[4], rank, num_procs);
+  points = read_points (buffer, conn->num_trees, &num_points);
+  SC_LDEBUGF ("Read %lld points\n", (long long) num_points);
+
+  p4est = p4est_new_points (mpicomm, conn, maxlevel, points,
+                            num_points, max_points, 5, NULL, NULL);
+  P4EST_FREE (points);
+  p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_points_created");
+
+  p4est_partition (p4est, 0, NULL);
+  p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_points_partition");
+
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+  p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_points_balance");
+
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (conn);
+
+  /* clean up and exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/points/points3.c b/example/points/points3.c
new file mode 100644
index 0000000..bb50664
--- /dev/null
+++ b/example/points/points3.c
@@ -0,0 +1,43 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/********************************************************************
+ *                          IMPORTANT NOTE                          *
+ *                                                                  *
+ * The p4est_points functionality depends on sc/src/sc_sort.        *
+ * That parallel bitonic sort is still buggy (see sc/bugs).         *
+ * If you want to use this code you have to fix the sort first.     *
+ ********************************************************************/
+
+/*
+ * Usage: p8est_points <configuration> <level> <prefix>
+ *        possible configurations:
+ *        o unit      The unit cube.
+ *        o periodic  The unit cube with all-periodic boundary conditions.
+ *        o rotwrap   The unit cube with various self-periodic b.c.
+ *        o twocubes  Two connected cubes.
+ *        o rotcubes  A collection of four connected rotated cubes.
+ */
+
+#include <p4est_to_p8est.h>
+#include "points2.c"
diff --git a/example/simple/Makefile.am b/example/simple/Makefile.am
new file mode 100644
index 0000000..9c78f66
--- /dev/null
+++ b/example/simple/Makefile.am
@@ -0,0 +1,18 @@
+
+# This file is part of p4est.
+# Makefile.am in example/simple
+# included non-recursively from toplevel directory
+
+if P4EST_ENABLE_BUILD_2D
+bin_PROGRAMS += example/simple/p4est_simple
+example_simple_p4est_simple_SOURCES = example/simple/simple2.c
+
+LINT_CSOURCES += $(example_simple_p4est_simple_SOURCES)
+endif
+
+if P4EST_ENABLE_BUILD_3D
+bin_PROGRAMS += example/simple/p8est_simple
+example_simple_p8est_simple_SOURCES = example/simple/simple3.c
+
+LINT_CSOURCES += $(example_simple_p8est_simple_SOURCES)
+endif
diff --git a/example/simple/simple2.c b/example/simple/simple2.c
new file mode 100644
index 0000000..b1132ef
--- /dev/null
+++ b/example/simple/simple2.c
@@ -0,0 +1,370 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * Usage: p4est_simple <configuration> <level>
+ *        possible configurations:
+ *        o unit      Refinement on the unit square.
+ *        o three     Refinement on a forest with three trees.
+ *        o evil      Check second round of refinement with np=5 level=7
+ *        o evil3     Check second round of refinement on three trees
+ *        o pillow    Refinement on a 2-tree pillow-shaped domain.
+ *        o moebius   Refinement on a 5-tree Moebius band.
+ *        o star      Refinement on a 6-tree star shaped domain.
+ *        o cubed     Refinement on a 6-tree cubed sphere surface.
+ *        o disk      Refinement on a 5-tree spherical disk.
+ *        o periodic  Refinement on the unit square with all-periodic b.c.
+ *        o rotwrap   Refinement on the unit square with weird periodic b.c.
+ */
+
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_vtk.h>
+
+typedef enum
+{
+  P4EST_CONFIG_NULL,
+  P4EST_CONFIG_UNIT,
+  P4EST_CONFIG_THREE,
+  P4EST_CONFIG_EVIL,
+  P4EST_CONFIG_EVIL3,
+  P4EST_CONFIG_PILLOW,
+  P4EST_CONFIG_MOEBIUS,
+  P4EST_CONFIG_STAR,
+  P4EST_CONFIG_CUBED,
+  P4EST_CONFIG_DISK,
+  P4EST_CONFIG_PERIODIC,
+  P4EST_CONFIG_ROTWRAP
+}
+simple_config_t;
+
+typedef struct
+{
+  simple_config_t     config;
+  int                 mpisize;
+  int                 level;
+  unsigned            checksum;
+}
+simple_regression_t;
+
+typedef struct
+{
+  p4est_topidx_t      a;
+}
+user_data_t;
+
+typedef struct
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpisize;
+  int                 mpirank;
+}
+mpi_context_t;
+
+static int          refine_level = 0;
+
+/* *INDENT-OFF* */
+static const simple_regression_t regression[] =
+{{ P4EST_CONFIG_THREE, 1, 7, 0xa8d85863U },
+ { P4EST_CONFIG_THREE, 2, 7, 0xa8d85863U },
+ { P4EST_CONFIG_THREE, 3, 7, 0xa8d85863U },
+ { P4EST_CONFIG_THREE, 4, 7, 0x20fb58edU },
+ { P4EST_CONFIG_MOEBIUS, 1, 6, 0x98ab6cb2U },
+ { P4EST_CONFIG_MOEBIUS, 3, 6, 0x98ab6cb2U },
+ { P4EST_CONFIG_MOEBIUS, 5, 6, 0x98ab6cb2U },
+ { P4EST_CONFIG_MOEBIUS, 6, 6, 0x6d2d6d6cU },
+ { P4EST_CONFIG_STAR, 5, 6, 0x38d3736fU },
+ { P4EST_CONFIG_STAR, 5, 7, 0xfb97aadfU },
+ { P4EST_CONFIG_CUBED, 4, 3, 0x85581649U },
+ { P4EST_CONFIG_CUBED, 5, 5, 0x64a1d105U },
+ { P4EST_CONFIG_DISK, 5, 4, 0x4995411dU },
+ { P4EST_CONFIG_DISK, 2, 6, 0x3f758706U },
+ { P4EST_CONFIG_ROTWRAP, 1, 6, 0x9dd600c5U },
+ { P4EST_CONFIG_ROTWRAP, 3, 6, 0x9dd600c5U },
+ { P4EST_CONFIG_NULL, 0, 0, 0 }};
+/* *INDENT-ON* */
+
+static void
+init_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+         p4est_quadrant_t * quadrant)
+{
+  user_data_t        *data = (user_data_t *) quadrant->p.user_data;
+
+  data->a = which_tree;
+}
+
+static int
+refine_normal_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+                  p4est_quadrant_t * quadrant)
+{
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && p4est_quadrant_child_id (quadrant) == 3) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_LAST_OFFSET (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->x >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int
+refine_evil_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+                p4est_quadrant_t * quadrant)
+{
+  if ((int) quadrant->level >= refine_level) {
+    return 0;
+  }
+  if (p4est->mpirank <= 1) {
+    return 1;
+  }
+
+  return 0;
+}
+
+static int
+refine_evil3_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+                 p4est_quadrant_t * quadrant)
+{
+  p4est_qcoord_t      u2;
+  p4est_quadrant_t    ref;
+
+  P4EST_QUADRANT_INIT (&ref);
+
+  u2 = P4EST_QUADRANT_LEN (2);
+
+  if (which_tree == 0) {
+    ref.x = 3 * u2;
+    ref.y = 2 * u2;
+  }
+  else if (which_tree == 1) {
+    ref.x = 2 * u2;
+    ref.y = 3 * u2;
+  }
+  ref.level = 2;
+
+  if ((int) quadrant->level >= refine_level) {
+    return 0;
+  }
+  if ((which_tree == 0 || which_tree == 1) &&
+      (p4est_quadrant_is_equal (&ref, quadrant) ||
+       p4est_quadrant_is_ancestor (&ref, quadrant))) {
+    return 1;
+  }
+
+  return 0;
+}
+
+static int
+coarsen_evil_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+                 p4est_quadrant_t * q[])
+{
+  if (p4est->mpirank >= 2) {
+    return 1;
+  }
+
+  return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 wrongusage;
+  unsigned            crc;
+  const char         *usage;
+  mpi_context_t       mpi_context, *mpi = &mpi_context;
+  p4est_t            *p4est;
+  p4est_connectivity_t *connectivity;
+  p4est_refine_t      refine_fn;
+  p4est_coarsen_t     coarsen_fn;
+  simple_config_t     config;
+  const simple_regression_t *r;
+
+  /* initialize MPI and p4est internals */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpi->mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpi->mpicomm, &mpi->mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpi->mpicomm, &mpi->mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpi->mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* process command line arguments */
+  usage =
+    "Arguments: <configuration> <level>\n"
+    "   Configuration can be any of\n"
+    "      unit|three|evil|evil3|pillow|moebius|\n"
+    "         star|cubed|disk|periodic|rotwrap\n"
+    "   Level controls the maximum depth of refinement\n";
+  wrongusage = 0;
+  config = P4EST_CONFIG_NULL;
+  if (!wrongusage && argc != 3) {
+    wrongusage = 1;
+  }
+  if (!wrongusage) {
+    if (!strcmp (argv[1], "unit")) {
+      config = P4EST_CONFIG_UNIT;
+    }
+    else if (!strcmp (argv[1], "three")) {
+      config = P4EST_CONFIG_THREE;
+    }
+    else if (!strcmp (argv[1], "evil")) {
+      config = P4EST_CONFIG_EVIL;
+    }
+    else if (!strcmp (argv[1], "evil3")) {
+      config = P4EST_CONFIG_EVIL3;
+    }
+    else if (!strcmp (argv[1], "pillow")) {
+      config = P4EST_CONFIG_PILLOW;
+    }
+    else if (!strcmp (argv[1], "moebius")) {
+      config = P4EST_CONFIG_MOEBIUS;
+    }
+    else if (!strcmp (argv[1], "star")) {
+      config = P4EST_CONFIG_STAR;
+    }
+    else if (!strcmp (argv[1], "cubed")) {
+      config = P4EST_CONFIG_CUBED;
+    }
+    else if (!strcmp (argv[1], "disk")) {
+      config = P4EST_CONFIG_DISK;
+    }
+    else if (!strcmp (argv[1], "periodic")) {
+      config = P4EST_CONFIG_PERIODIC;
+    }
+    else if (!strcmp (argv[1], "rotwrap")) {
+      config = P4EST_CONFIG_ROTWRAP;
+    }
+    else {
+      wrongusage = 1;
+    }
+  }
+  if (wrongusage) {
+    P4EST_GLOBAL_LERROR (usage);
+    sc_abort_collective ("Usage error");
+  }
+
+  /* assign variables based on configuration */
+  refine_level = atoi (argv[2]);
+  if (config == P4EST_CONFIG_EVIL) {
+    refine_fn = refine_evil_fn;
+    coarsen_fn = coarsen_evil_fn;
+  }
+  else if (config == P4EST_CONFIG_EVIL3) {
+    refine_fn = refine_evil3_fn;
+    coarsen_fn = NULL;
+  }
+  else {
+    refine_fn = refine_normal_fn;
+    coarsen_fn = NULL;
+  }
+
+  /* create connectivity and forest structures */
+  if (config == P4EST_CONFIG_THREE || config == P4EST_CONFIG_EVIL3) {
+    connectivity = p4est_connectivity_new_corner ();
+  }
+  else if (config == P4EST_CONFIG_PILLOW) {
+    connectivity = p4est_connectivity_new_pillow ();
+  }
+  else if (config == P4EST_CONFIG_MOEBIUS) {
+    connectivity = p4est_connectivity_new_moebius ();
+  }
+  else if (config == P4EST_CONFIG_STAR) {
+    connectivity = p4est_connectivity_new_star ();
+  }
+  else if (config == P4EST_CONFIG_CUBED) {
+    connectivity = p4est_connectivity_new_cubed ();
+  }
+  else if (config == P4EST_CONFIG_DISK) {
+    connectivity = p4est_connectivity_new_disk ();
+  }
+  else if (config == P4EST_CONFIG_PERIODIC) {
+    connectivity = p4est_connectivity_new_periodic ();
+  }
+  else if (config == P4EST_CONFIG_ROTWRAP) {
+    connectivity = p4est_connectivity_new_rotwrap ();
+  }
+  else {
+    connectivity = p4est_connectivity_new_unitsquare ();
+  }
+  p4est = p4est_new_ext (mpi->mpicomm, connectivity, 15, 0, 0,
+                         sizeof (user_data_t), init_fn, NULL);
+  p4est_vtk_write_file (p4est, NULL, "simple2_new");
+
+  /* refinement and coarsening */
+  p4est_refine (p4est, 1, refine_fn, init_fn);
+  if (coarsen_fn != NULL) {
+    p4est_coarsen (p4est, 1, coarsen_fn, init_fn);
+  }
+  p4est_vtk_write_file (p4est, NULL, "simple2_refined");
+
+  /* balance */
+  p4est_balance (p4est, P4EST_CONNECT_FULL, init_fn);
+  p4est_vtk_write_file (p4est, NULL, "simple2_balanced");
+  crc = p4est_checksum (p4est);
+
+  /* partition */
+  p4est_partition (p4est, 0, NULL);
+  p4est_vtk_write_file (p4est, NULL, "simple2_partition");
+
+#ifdef P4EST_ENABLE_DEBUG
+  /* rebalance should not change checksum */
+  p4est_balance (p4est, P4EST_CONNECT_FULL, init_fn);
+  P4EST_ASSERT (p4est_checksum (p4est) == crc);
+#endif
+
+  /* print and verify forest checksum */
+  P4EST_GLOBAL_STATISTICSF ("Tree checksum 0x%08x\n", crc);
+  if (mpi->mpirank == 0) {
+    for (r = regression; r->config != P4EST_CONFIG_NULL; ++r) {
+      if (r->config != config || r->mpisize != mpi->mpisize
+          || r->level != refine_level)
+        continue;
+      SC_CHECK_ABORT (crc == r->checksum, "Checksum mismatch");
+      P4EST_GLOBAL_INFO ("Checksum regression OK\n");
+      break;
+    }
+  }
+
+  /* destroy the p4est and its connectivity structure */
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (connectivity);
+
+  /* clean up and exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/simple/simple3.c b/example/simple/simple3.c
new file mode 100644
index 0000000..66145fc
--- /dev/null
+++ b/example/simple/simple3.c
@@ -0,0 +1,332 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * Usage: p8est_simple <configuration> <level>
+ *        possible configurations:
+ *        o unit      The unit cube.
+ *        o periodic  The unit cube with all-periodic boundary conditions.
+ *        o rotwrap   The unit cube with various self-periodic b.c.
+ *        o twocubes  Two connected cubes.
+ *        o twowrap   Two cubes with periodically identified far ends.
+ *        o rotcubes  A collection of six connected rotated cubes.
+ *        o shell     A 24-tree discretization of a hollow sphere.
+ *        o sphere    A 13-tree discretization of a solid sphere.
+ */
+
+#define VTK_OUTPUT 1
+
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+
+#ifdef VTK_OUTPUT
+#include <p8est_vtk.h>
+#endif
+
+typedef enum
+{
+  P8EST_CONFIG_NULL,
+  P8EST_CONFIG_UNIT,
+  P8EST_CONFIG_PERIODIC,
+  P8EST_CONFIG_ROTWRAP,
+  P8EST_CONFIG_TWOCUBES,
+  P8EST_CONFIG_TWOWRAP,
+  P8EST_CONFIG_ROTCUBES,
+  P8EST_CONFIG_SHELL,
+  P8EST_CONFIG_SPHERE
+}
+simple_config_t;
+
+typedef struct
+{
+  simple_config_t     config;
+  int                 mpisize;
+  int                 level;
+  unsigned            checksum;
+}
+simple_regression_t;
+
+typedef struct
+{
+  p4est_topidx_t      a;
+}
+user_data_t;
+
+typedef struct
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpisize;
+  int                 mpirank;
+}
+mpi_context_t;
+
+static int          refine_level = 0;
+
+/* *INDENT-OFF* */
+static const simple_regression_t regression[] =
+{{ P8EST_CONFIG_UNIT, 1, 7, 0x88fc2229U },
+ { P8EST_CONFIG_UNIT, 3, 6, 0xce19fee3U },
+ { P8EST_CONFIG_TWOCUBES, 1, 4, 0xd9e96b31U },
+ { P8EST_CONFIG_TWOCUBES, 3, 5, 0xe8b16b4aU },
+ { P8EST_CONFIG_TWOWRAP, 1, 4, 0xd3e06e2fU },
+ { P8EST_CONFIG_TWOWRAP, 5, 5, 0x920ecd43U },
+ { P8EST_CONFIG_PERIODIC, 1, 4, 0x28304c83U },
+ { P8EST_CONFIG_PERIODIC, 7, 4, 0x28304c83U },
+ { P8EST_CONFIG_PERIODIC, 3, 5, 0xe4d123b2U },
+ { P8EST_CONFIG_PERIODIC, 6, 6, 0x81c22cc6U },
+ { P8EST_CONFIG_ROTWRAP, 1, 5, 0xe4d123b2U },
+ { P8EST_CONFIG_ROTWRAP, 3, 5, 0xe4d123b2U },
+ { P8EST_CONFIG_ROTWRAP, 5, 6, 0x81c22cc6U },
+ { P8EST_CONFIG_ROTCUBES, 1, 5, 0x5c497bdaU },
+ { P8EST_CONFIG_ROTCUBES, 3, 5, 0x5c497bdaU },
+ { P8EST_CONFIG_ROTCUBES, 5, 6, 0x00530556U },
+ { P8EST_CONFIG_ROTCUBES, 7, 1, 0x47f00071U },
+ { P8EST_CONFIG_ROTCUBES, 7, 6, 0x00530556U },
+ { P8EST_CONFIG_ROTCUBES, 7, 7, 0x84730f31U },
+ { P8EST_CONFIG_ROTCUBES, 9, 1, 0x00600001U },
+ { P8EST_CONFIG_NULL, 0, 0, 0 }};
+/* *INDENT-ON* */
+
+static void
+init_fn (p8est_t * p8est, p4est_topidx_t which_tree,
+         p8est_quadrant_t * quadrant)
+{
+  user_data_t        *data = (user_data_t *) quadrant->p.user_data;
+
+  data->a = which_tree;
+}
+
+static int
+refine_sparse_fn (p8est_t * p8est, p4est_topidx_t which_tree,
+                  p8est_quadrant_t * quadrant)
+{
+  if (which_tree != 0) {
+    return 0;
+  }
+  if ((int) quadrant->level >= refine_level) {
+    return 0;
+  }
+  if (quadrant->level == 0) {
+    return 1;
+  }
+  if (quadrant->x < P8EST_QUADRANT_LEN (2) &&
+      quadrant->y > 0 && quadrant->z < P8EST_QUADRANT_LEN (2)) {
+    return 1;
+  }
+
+  return 0;
+}
+
+static int
+refine_normal_fn (p8est_t * p8est, p4est_topidx_t which_tree,
+                  p8est_quadrant_t * quadrant)
+{
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && p8est_quadrant_child_id (quadrant) == 3) {
+    return 1;
+  }
+  if (quadrant->x == P8EST_LAST_OFFSET (2) &&
+      quadrant->y == P8EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->z >= P8EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 wrongusage;
+  unsigned            crc;
+  const char         *usage;
+  mpi_context_t       mpi_context, *mpi = &mpi_context;
+  p8est_t            *p8est;
+  p8est_connectivity_t *connectivity;
+  p8est_geometry_t   *geom;
+  p8est_refine_t      refine_fn;
+  p8est_coarsen_t     coarsen_fn;
+  simple_config_t     config;
+  const simple_regression_t *r;
+
+  /* initialize MPI and p4est internals */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpi->mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpi->mpicomm, &mpi->mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpi->mpicomm, &mpi->mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpi->mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* process command line arguments */
+  usage =
+    "Arguments: <configuration> <level>\n"
+    "   Configuration can be any of\n"
+    "      unit|periodic|rotwrap|twocubes|twowrap|rotcubes|shell|sphere\n"
+    "   Level controls the maximum depth of refinement\n";
+  wrongusage = 0;
+  config = P8EST_CONFIG_NULL;
+  if (!wrongusage && argc != 3) {
+    wrongusage = 1;
+  }
+  if (!wrongusage) {
+    if (!strcmp (argv[1], "unit")) {
+      config = P8EST_CONFIG_UNIT;
+    }
+    else if (!strcmp (argv[1], "periodic")) {
+      config = P8EST_CONFIG_PERIODIC;
+    }
+    else if (!strcmp (argv[1], "rotwrap")) {
+      config = P8EST_CONFIG_ROTWRAP;
+    }
+    else if (!strcmp (argv[1], "twocubes")) {
+      config = P8EST_CONFIG_TWOCUBES;
+    }
+    else if (!strcmp (argv[1], "twowrap")) {
+      config = P8EST_CONFIG_TWOWRAP;
+    }
+    else if (!strcmp (argv[1], "rotcubes")) {
+      config = P8EST_CONFIG_ROTCUBES;
+    }
+    else if (!strcmp (argv[1], "shell")) {
+      config = P8EST_CONFIG_SHELL;
+    }
+    else if (!strcmp (argv[1], "sphere")) {
+      config = P8EST_CONFIG_SPHERE;
+    }
+    else {
+      wrongusage = 1;
+    }
+  }
+  if (wrongusage) {
+    P4EST_GLOBAL_LERROR (usage);
+    sc_abort_collective ("Usage error");
+  }
+
+  /* assign variables based on configuration */
+  refine_level = atoi (argv[2]);
+  refine_fn = refine_normal_fn;
+  coarsen_fn = NULL;
+
+  /* create connectivity and forest structures */
+  geom = NULL;
+  if (config == P8EST_CONFIG_PERIODIC) {
+    connectivity = p8est_connectivity_new_periodic ();
+  }
+  else if (config == P8EST_CONFIG_ROTWRAP) {
+    connectivity = p8est_connectivity_new_rotwrap ();
+  }
+  else if (config == P8EST_CONFIG_TWOCUBES) {
+    connectivity = p8est_connectivity_new_twocubes ();
+    refine_fn = refine_sparse_fn;
+  }
+  else if (config == P8EST_CONFIG_TWOWRAP) {
+    connectivity = p8est_connectivity_new_twowrap ();
+    refine_fn = refine_sparse_fn;
+  }
+  else if (config == P8EST_CONFIG_ROTCUBES) {
+    connectivity = p8est_connectivity_new_rotcubes ();
+  }
+  else if (config == P8EST_CONFIG_SHELL) {
+    connectivity = p8est_connectivity_new_shell ();
+    geom = p8est_geometry_new_shell (connectivity, 1., .55);
+  }
+  else if (config == P8EST_CONFIG_SPHERE) {
+    connectivity = p8est_connectivity_new_sphere ();
+    geom = p8est_geometry_new_sphere (connectivity, 1., 0.191728, 0.039856);
+  }
+  else {
+    connectivity = p8est_connectivity_new_unitcube ();
+  }
+  p8est = p8est_new_ext (mpi->mpicomm, connectivity, 1, 0, 0,
+                         sizeof (user_data_t), init_fn, NULL);
+
+#ifdef VTK_OUTPUT
+  p8est_vtk_write_file (p8est, geom, "simple3_new");
+#endif
+
+  /* refinement and coarsening */
+  p8est_refine (p8est, 1, refine_fn, init_fn);
+  if (coarsen_fn != NULL) {
+    p8est_coarsen (p8est, 1, coarsen_fn, init_fn);
+  }
+#ifdef VTK_OUTPUT
+  p8est_vtk_write_file (p8est, geom, "simple3_refined");
+#endif
+
+  /* balance */
+  p8est_balance (p8est, P8EST_CONNECT_FULL, init_fn);
+#ifdef VTK_OUTPUT
+  p8est_vtk_write_file (p8est, geom, "simple3_balanced");
+#endif
+
+  crc = p8est_checksum (p8est);
+
+  /* partition */
+  p8est_partition (p8est, 0, NULL);
+#ifdef VTK_OUTPUT
+  p8est_vtk_write_file (p8est, geom, "simple3_partition");
+#endif
+
+#ifdef P4EST_ENABLE_DEBUG
+  /* rebalance should not change checksum */
+  p8est_balance (p8est, P8EST_CONNECT_FULL, init_fn);
+  P4EST_ASSERT (p8est_checksum (p8est) == crc);
+#endif
+
+  /* print and verify forest checksum */
+  P4EST_GLOBAL_STATISTICSF ("Tree checksum 0x%08x\n", crc);
+  if (mpi->mpirank == 0) {
+    for (r = regression; r->config != P8EST_CONFIG_NULL; ++r) {
+      if (r->config != config || r->mpisize != mpi->mpisize
+          || r->level != refine_level)
+        continue;
+      SC_CHECK_ABORT (crc == r->checksum, "Checksum mismatch");
+      P4EST_GLOBAL_INFO ("Checksum regression OK\n");
+      break;
+    }
+  }
+
+  /* destroy the p8est and its connectivity structure */
+  p8est_destroy (p8est);
+  if (geom != NULL) {
+    p8est_geometry_destroy (geom);
+  }
+  p8est_connectivity_destroy (connectivity);
+
+  /* clean up and exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/steps/Makefile.am b/example/steps/Makefile.am
new file mode 100644
index 0000000..bc1b8f7
--- /dev/null
+++ b/example/steps/Makefile.am
@@ -0,0 +1,51 @@
+
+# This file is part of p4est.
+# Makefile.am in example/steps
+# included non-recursively from toplevel directory
+
+if P4EST_ENABLE_BUILD_2D
+bin_PROGRAMS += \
+  example/steps/p4est_step1 \
+  example/steps/p4est_step2 \
+  example/steps/p4est_step3 \
+  example/steps/p4est_step4
+example_steps_p4est_step1_SOURCES = example/steps/p4est_step1.c
+example_steps_p4est_step2_SOURCES = example/steps/p4est_step2.c
+example_steps_p4est_step3_SOURCES = example/steps/p4est_step3.c
+example_steps_p4est_step4_SOURCES = example/steps/p4est_step4.c
+
+LINT_CSOURCES += $(example_steps_p4est_step1_SOURCES) \
+                 $(example_steps_p4est_step2_SOURCES) \
+                 $(example_steps_p4est_step3_SOURCES) \
+                 $(example_steps_p4est_step4_SOURCES)
+endif
+
+if P4EST_ENABLE_BUILD_3D
+bin_PROGRAMS += \
+  example/steps/p8est_step1 \
+  example/steps/p8est_step2 \
+  example/steps/p8est_step3 \
+  example/steps/p8est_step4
+example_steps_p8est_step1_SOURCES = example/steps/p8est_step1.c
+example_steps_p8est_step2_SOURCES = example/steps/p8est_step2.c
+example_steps_p8est_step3_SOURCES = example/steps/p8est_step3.c
+example_steps_p8est_step4_SOURCES = example/steps/p8est_step4.c
+
+LINT_CSOURCES += $(example_steps_p8est_step1_SOURCES) \
+                 $(example_steps_p8est_step2_SOURCES) \
+                 $(example_steps_p8est_step3_SOURCES) \
+                 $(example_steps_p8est_step4_SOURCES)
+endif
+
+EXTRA_DIST += \
+  example/steps/hw32.h 
+
+dist_p4estdata_DATA +=  \
+  example/steps/hole_2d_cubit.inp \
+  example/steps/hole_2d_cubit.jou \
+  example/steps/hole_3d_cubit.inp \
+  example/steps/hole_3d_cubit.jou \
+  example/steps/hole_2d_gmsh.inp \
+  example/steps/hole_2d_gmsh.geo \
+  example/steps/hole_3d_gmsh.inp \
+  example/steps/hole_3d_gmsh.geo
diff --git a/example/steps/hole_2d_cubit.inp b/example/steps/hole_2d_cubit.inp
new file mode 100644
index 0000000..bdb3919
--- /dev/null
+++ b/example/steps/hole_2d_cubit.inp
@@ -0,0 +1,272 @@
+*HEADING
+cubit(/Users/lucas/hole_2d_cubit.inp): 07/01/2014: 07:06:43
+version: 14.0
+**
+********************************** P A R T S **********************************
+*PART, NAME=Part-Default
+**
+********************************** N O D E S **********************************
+*NODE, NSET=ALLNODES
+       1,    3.000000e+00,    0.000000e+00,    0.000000e+00
+       2,    2.897777e+00,   -7.764571e-01,    0.000000e+00
+       3,    2.598076e+00,   -1.500000e+00,    0.000000e+00
+       4,    2.121320e+00,   -2.121320e+00,    0.000000e+00
+       5,    1.500000e+00,   -2.598076e+00,    0.000000e+00
+       6,    7.764571e-01,   -2.897777e+00,    0.000000e+00
+       7,    1.836970e-16,   -3.000000e+00,    0.000000e+00
+       8,   -7.764571e-01,   -2.897777e+00,    0.000000e+00
+       9,   -1.500000e+00,   -2.598076e+00,    0.000000e+00
+      10,   -2.121320e+00,   -2.121320e+00,    0.000000e+00
+      11,   -2.598076e+00,   -1.500000e+00,    0.000000e+00
+      12,   -2.897777e+00,   -7.764571e-01,    0.000000e+00
+      13,   -3.000000e+00,   -3.673940e-16,    0.000000e+00
+      14,   -2.897777e+00,    7.764571e-01,    0.000000e+00
+      15,   -2.598076e+00,    1.500000e+00,    0.000000e+00
+      16,   -2.121320e+00,    2.121320e+00,    0.000000e+00
+      17,   -1.500000e+00,    2.598076e+00,    0.000000e+00
+      18,   -7.764571e-01,    2.897777e+00,    0.000000e+00
+      19,   -5.510911e-16,    3.000000e+00,    0.000000e+00
+      20,    7.764571e-01,    2.897777e+00,    0.000000e+00
+      21,    1.500000e+00,    2.598076e+00,    0.000000e+00
+      22,    2.121320e+00,    2.121320e+00,    0.000000e+00
+      23,    2.598076e+00,    1.500000e+00,    0.000000e+00
+      24,    2.897777e+00,    7.764571e-01,    0.000000e+00
+      25,    5.000000e+00,    5.000000e+00,    0.000000e+00
+      26,   -5.000000e+00,    5.000000e+00,    0.000000e+00
+      27,    4.166667e+00,    5.000000e+00,    0.000000e+00
+      28,    3.333333e+00,    5.000000e+00,    0.000000e+00
+      29,    2.500000e+00,    5.000000e+00,    0.000000e+00
+      30,    1.666667e+00,    5.000000e+00,    0.000000e+00
+      31,    8.333333e-01,    5.000000e+00,    0.000000e+00
+      32,    0.000000e+00,    5.000000e+00,    0.000000e+00
+      33,   -8.333333e-01,    5.000000e+00,    0.000000e+00
+      34,   -1.666667e+00,    5.000000e+00,    0.000000e+00
+      35,   -2.500000e+00,    5.000000e+00,    0.000000e+00
+      36,   -3.333333e+00,    5.000000e+00,    0.000000e+00
+      37,   -4.166667e+00,    5.000000e+00,    0.000000e+00
+      38,   -5.000000e+00,   -5.000000e+00,    0.000000e+00
+      39,   -5.000000e+00,    4.166667e+00,    0.000000e+00
+      40,   -5.000000e+00,    3.333333e+00,    0.000000e+00
+      41,   -5.000000e+00,    2.500000e+00,    0.000000e+00
+      42,   -5.000000e+00,    1.666667e+00,    0.000000e+00
+      43,   -5.000000e+00,    8.333333e-01,    0.000000e+00
+      44,   -5.000000e+00,    0.000000e+00,    0.000000e+00
+      45,   -5.000000e+00,   -8.333333e-01,    0.000000e+00
+      46,   -5.000000e+00,   -1.666667e+00,    0.000000e+00
+      47,   -5.000000e+00,   -2.500000e+00,    0.000000e+00
+      48,   -5.000000e+00,   -3.333333e+00,    0.000000e+00
+      49,   -5.000000e+00,   -4.166667e+00,    0.000000e+00
+      50,    5.000000e+00,   -5.000000e+00,    0.000000e+00
+      51,   -4.166667e+00,   -5.000000e+00,    0.000000e+00
+      52,   -3.333333e+00,   -5.000000e+00,    0.000000e+00
+      53,   -2.500000e+00,   -5.000000e+00,    0.000000e+00
+      54,   -1.666667e+00,   -5.000000e+00,    0.000000e+00
+      55,   -8.333333e-01,   -5.000000e+00,    0.000000e+00
+      56,    0.000000e+00,   -5.000000e+00,    0.000000e+00
+      57,    8.333333e-01,   -5.000000e+00,    0.000000e+00
+      58,    1.666667e+00,   -5.000000e+00,    0.000000e+00
+      59,    2.500000e+00,   -5.000000e+00,    0.000000e+00
+      60,    3.333333e+00,   -5.000000e+00,    0.000000e+00
+      61,    4.166667e+00,   -5.000000e+00,    0.000000e+00
+      62,    5.000000e+00,   -4.166667e+00,    0.000000e+00
+      63,    5.000000e+00,   -3.333333e+00,    0.000000e+00
+      64,    5.000000e+00,   -2.500000e+00,    0.000000e+00
+      65,    5.000000e+00,   -1.666667e+00,    0.000000e+00
+      66,    5.000000e+00,   -8.333333e-01,    0.000000e+00
+      67,    5.000000e+00,    0.000000e+00,    0.000000e+00
+      68,    5.000000e+00,    8.333333e-01,    0.000000e+00
+      69,    5.000000e+00,    1.666667e+00,    0.000000e+00
+      70,    5.000000e+00,    2.500000e+00,    0.000000e+00
+      71,    5.000000e+00,    3.333333e+00,    0.000000e+00
+      72,    5.000000e+00,    4.166667e+00,    0.000000e+00
+      73,    3.928506e+00,   -1.482989e-04,    0.000000e+00
+      74,    1.911494e+00,   -3.623480e+00,    0.000000e+00
+      75,    8.867506e-01,   -3.870130e+00,    0.000000e+00
+      76,    2.848964e-05,   -3.926921e+00,    0.000000e+00
+      77,   -2.729042e+00,   -2.725144e+00,    0.000000e+00
+      78,   -3.622260e+00,   -1.911007e+00,    0.000000e+00
+      79,   -3.868900e+00,   -8.872274e-01,    0.000000e+00
+      80,   -3.926564e+00,   -1.867606e-04,    0.000000e+00
+      81,   -2.725096e+00,    2.729117e+00,    0.000000e+00
+      82,   -1.911025e+00,    3.622284e+00,    0.000000e+00
+      83,   -8.873643e-01,    3.868917e+00,    0.000000e+00
+      84,   -2.647280e-04,    3.926565e+00,    0.000000e+00
+      85,    2.729978e+00,    2.724175e+00,    0.000000e+00
+      86,    3.623299e+00,    1.910616e+00,    0.000000e+00
+      87,    3.872296e+00,    8.851222e-01,    0.000000e+00
+      88,    4.238322e+00,    4.237499e+00,    0.000000e+00
+      89,    3.523905e+00,    4.241766e+00,    0.000000e+00
+      90,    2.802653e+00,    4.021893e+00,    0.000000e+00
+      91,    1.916256e+00,    3.618427e+00,    0.000000e+00
+      92,    8.903040e-01,    3.860254e+00,    0.000000e+00
+      93,   -4.237293e+00,    4.238731e+00,    0.000000e+00
+      94,   -4.241640e+00,    3.524071e+00,    0.000000e+00
+      95,   -4.021966e+00,    2.802619e+00,    0.000000e+00
+      96,   -3.618581e+00,    1.916174e+00,    0.000000e+00
+      97,   -3.860283e+00,    8.903086e-01,    0.000000e+00
+      98,   -4.238726e+00,   -4.237364e+00,    0.000000e+00
+      99,   -3.524037e+00,   -4.241729e+00,    0.000000e+00
+     100,   -2.802595e+00,   -4.022031e+00,    0.000000e+00
+     101,   -1.916171e+00,   -3.618628e+00,    0.000000e+00
+     102,   -8.903585e-01,   -3.860396e+00,    0.000000e+00
+     103,    2.801426e+00,   -4.021301e+00,    0.000000e+00
+     104,    3.526455e+00,   -4.239697e+00,    0.000000e+00
+     105,    4.240788e+00,   -4.235934e+00,    0.000000e+00
+     106,   -2.800886e+00,    4.022777e+00,    0.000000e+00
+     107,   -3.522706e+00,    4.240234e+00,    0.000000e+00
+     108,    4.242906e+00,   -3.521428e+00,    0.000000e+00
+     109,    2.727482e+00,   -2.728716e+00,    0.000000e+00
+     110,    3.618870e+00,   -1.915098e+00,    0.000000e+00
+     111,    3.865070e+00,   -8.858252e-01,    0.000000e+00
+     112,   -4.022766e+00,   -2.800952e+00,    0.000000e+00
+     113,   -4.240218e+00,   -3.522774e+00,    0.000000e+00
+     114,   -3.682199e+00,    3.684840e+00,    0.000000e+00
+     115,   -3.279098e+00,    3.286126e+00,    0.000000e+00
+     116,    4.022576e+00,    2.801151e+00,    0.000000e+00
+     117,    4.239966e+00,    3.522904e+00,    0.000000e+00
+     118,    3.684513e+00,    3.682443e+00,    0.000000e+00
+     119,    3.286239e+00,    3.279200e+00,    0.000000e+00
+     120,   -3.684815e+00,   -3.682312e+00,    0.000000e+00
+     121,   -3.286084e+00,   -3.279176e+00,    0.000000e+00
+     122,    3.687735e+00,   -3.681205e+00,    0.000000e+00
+     123,    3.286039e+00,   -3.284771e+00,    0.000000e+00
+     124,    4.024813e+00,   -2.799615e+00,    0.000000e+00
+**
+********************************** E L E M E N T S ****************************
+*ELEMENT, TYPE=S4R, ELSET=EB1
+       1,       1,       2,     111,      73
+       2,       2,       3,     110,     111
+       3,       3,       4,     109,     110
+       4,       4,       5,      74,     109
+       5,       5,       6,      75,      74
+       6,       6,       7,      76,      75
+       7,       7,       8,     102,      76
+       8,       8,       9,     101,     102
+       9,       9,      10,      77,     101
+      10,      10,      11,      78,      77
+      11,      11,      12,      79,      78
+      12,      12,      13,      80,      79
+      13,      13,      14,      97,      80
+      14,      14,      15,      96,      97
+      15,      15,      16,      81,      96
+      16,      16,      17,      82,      81
+      17,      17,      18,      83,      82
+      18,      18,      19,      84,      83
+      19,      19,      20,      92,      84
+      20,      20,      21,      91,      92
+      21,      21,      22,      85,      91
+      22,      22,      23,      86,      85
+      23,      23,      24,      87,      86
+      24,      24,       1,      73,      87
+      25,      25,      27,      88,      72
+      26,      27,      28,      89,      88
+      27,      28,      29,      90,      89
+      28,      29,      30,      91,      90
+      29,      30,      31,      92,      91
+      30,      26,      39,      93,      37
+      31,      39,      40,      94,      93
+      32,      40,      41,      95,      94
+      33,      41,      42,      96,      95
+      34,      42,      43,      97,      96
+      35,      43,      44,      80,      97
+      36,      31,      32,      84,      92
+      37,      38,      51,      98,      49
+      38,      51,      52,      99,      98
+      39,      52,      53,     100,      99
+      40,      53,      54,     101,     100
+      41,      54,      55,     102,     101
+      42,      55,      56,      76,     102
+      43,      56,      57,      75,      76
+      44,      32,      33,      83,      84
+      45,      57,      58,      74,      75
+      46,      33,      34,      82,      83
+      47,      58,      59,     103,      74
+      48,      59,      60,     104,     103
+      49,      60,      61,     105,     104
+      50,      61,      50,      62,     105
+      51,      34,      35,     106,      82
+      52,      35,      36,     107,     106
+      53,      36,      37,      93,     107
+      54,      44,      45,      79,      80
+      55,      62,      63,     108,     105
+      56,      65,      66,     111,     110
+      57,      45,      46,      78,      79
+      58,      66,      67,      73,     111
+      59,      46,      47,     112,      78
+      60,      47,      48,     113,     112
+      61,      48,      49,      98,     113
+      62,      67,      68,      87,      73
+      63,      93,      94,     114,     107
+      64,      94,      95,     115,     114
+      65,      95,      96,      81,     115
+      66,      68,      69,      86,      87
+      67,     115,      81,      82,     106
+      68,     106,     107,     114,     115
+      69,      69,      70,     116,      86
+      70,      70,      71,     117,     116
+      71,      71,      72,      88,     117
+      72,      86,     116,     119,      85
+      73,     116,     117,     118,     119
+      74,     117,      88,      89,     118
+      75,      89,      90,     119,     118
+      76,      91,      85,     119,      90
+      77,      98,      99,     120,     113
+      78,      99,     100,     121,     120
+      79,     100,     101,      77,     121
+      80,     121,      77,      78,     112
+      81,     112,     113,     120,     121
+      82,     105,     108,     122,     104
+      83,      74,     103,     123,     109
+      84,     103,     104,     122,     123
+      85,     124,     123,     122,     108
+      86,     124,     108,      63,      64
+      87,     124,      64,      65,     110
+      88,     124,     110,     109,     123
+**
+********************************** P R O P E R T I E S ************************
+*SHELL SECTION, ELSET=EB1, SECTION INTEGRATION=SIMPSON, MATERIAL=Default-Steel
+1.000000e+00
+**
+*END PART
+**
+**
+**
+********************************** E N D   P A R T S **********************************
+**
+**
+********************************** A S S E M B L Y ************************************
+**
+*ASSEMBLY, NAME=ASSEMBLY1
+**
+*INSTANCE, NAME=Part-Default_1, PART=Part-Default
+*END INSTANCE
+**
+*END ASSEMBLY
+**
+**
+**
+*MATERIAL, NAME = Default-Steel
+*ELASTIC, TYPE=ISOTROPIC
+2.068000e+05, 2.900000e-01
+*DENSITY
+7.000000e-06
+*CONDUCTIVITY,TYPE=ISO
+4.500000e-02
+*SPECIFIC HEAT
+5.000000e+02
+**
+**
+************************************** H I S T O R Y *************************************
+**
+*PREPRINT
+**
+**************************************** S T E P 1 ***************************************
+*STEP,INC=100,NAME=Default Set
+**
+*STATIC
+1, 1, 1e-05, 1
+**
+**
+**
+**
+*END STEP
diff --git a/example/steps/hole_2d_cubit.jou b/example/steps/hole_2d_cubit.jou
new file mode 100644
index 0000000..8bf8bae
--- /dev/null
+++ b/example/steps/hole_2d_cubit.jou
@@ -0,0 +1,9 @@
+#!python
+import os
+
+cubit.cmd('create surface rectangle width 10 zplane')
+cubit.cmd('create surface circle radius 3 zplane')
+cubit.cmd('subtract volume 2 from volume 1')
+cubit.cmd('surface 3 size auto factor 5')
+cubit.cmd('mesh surface 3')
+cubit.cmd("export Abaqus \"%s/hole_2d_cubit.inp\" overwrite cubitids"%(os.environ['HOME'],))
diff --git a/example/steps/hole_2d_gmsh.geo b/example/steps/hole_2d_gmsh.geo
new file mode 100644
index 0000000..d88eb17
--- /dev/null
+++ b/example/steps/hole_2d_gmsh.geo
@@ -0,0 +1,31 @@
+lc = 1;
+r1 = 5;
+r2 = 3;
+
+Point(1) = {-r1,-r1,0,lc};
+Point(2) = { r1,-r1,0,lc};
+Point(3) = { r1, r1,0,lc};
+Point(4) = {-r1, r1,0,lc};
+
+Point(5) = {  0,  0,0,lc};
+
+Point(6) = {  0,-r2,0,lc};
+Point(7) = {  0, r2,0,lc};
+Point(8) = { r2,  0,0,lc};
+Point(9) = {-r2,  0,0,lc};
+
+Line(1) = {1,2};
+Line(2) = {2,3};
+Line(3) = {3,4};
+Line(4) = {4,1};
+
+Circle(5) = {8, 5, 7};
+Circle(6) = {7, 5, 9};
+Circle(7) = {9, 5, 6};
+Circle(8) = {6, 5, 8};
+
+Line Loop(9)  = {1,2,3,4};
+Line Loop(10) = {5,6,7,8};
+
+Plane Surface(11) = {9, 10};
+Recombine Surface {11};
diff --git a/example/steps/hole_2d_gmsh.inp b/example/steps/hole_2d_gmsh.inp
new file mode 100644
index 0000000..580ee05
--- /dev/null
+++ b/example/steps/hole_2d_gmsh.inp
@@ -0,0 +1,301 @@
+*Heading
+ hole_2d_gmsh.inp
+*Node
+1, -5, -5, 0
+2, 5, -5, 0
+3, 5, 5, 0
+4, -5, 5, 0
+5, 0, 0, 0
+6, 0, -3, 0
+7, 0, 3, 0
+8, 3, 0, 0
+9, -3, 0, 0
+10, -3.999999999998766, -5, 0
+11, -3.000000000000863, -5, 0
+12, -2.00000000000407, -5, 0
+13, -1.00000000000724, -5, 0
+14, -6.145306485905166e-12, -5, 0
+15, 0.9999999999950866, -5, 0
+16, 1.999999999996321, -5, 0
+17, 2.999999999997555, -5, 0
+18, 3.999999999998776, -5, 0
+19, 5, -3.999999999998766, 0
+20, 5, -3.000000000000863, 0
+21, 5, -2.00000000000407, 0
+22, 5, -1.00000000000724, 0
+23, 5, -6.145306485905166e-12, 0
+24, 5, 0.9999999999950866, 0
+25, 5, 1.999999999996321, 0
+26, 5, 2.999999999997555, 0
+27, 5, 3.999999999998776, 0
+28, 3.999999999998766, 5, 0
+29, 3.000000000000863, 5, 0
+30, 2.00000000000407, 5, 0
+31, 1.00000000000724, 5, 0
+32, 6.145306485905166e-12, 5, 0
+33, -0.9999999999950866, 5, 0
+34, -1.999999999996321, 5, 0
+35, -2.999999999997555, 5, 0
+36, -3.999999999998776, 5, 0
+37, -5, 3.999999999998766, 0
+38, -5, 3.000000000000863, 0
+39, -5, 2.00000000000407, 0
+40, -5, 1.00000000000724, 0
+41, -5, 6.145306485905166e-12, 0
+42, -5, -0.9999999999950866, 0
+43, -5, -1.999999999996321, 0
+44, -5, -2.999999999997555, 0
+45, -5, -3.999999999998776, 0
+46, 2.897777478867742, 0.7764571353055585, 0
+47, 2.598076211355317, 1.499999999996534, 0
+48, 2.12132034356377, 2.121320343555515, 0
+49, 1.500000000003278, 2.598076211351423, 0
+50, 0.7764571353089083, 2.897777478866844, 0
+51, -0.7764571353055585, 2.897777478867742, 0
+52, -1.499999999996534, 2.598076211355317, 0
+53, -2.121320343555515, 2.12132034356377, 0
+54, -2.598076211351423, 1.500000000003278, 0
+55, -2.897777478866844, 0.7764571353089083, 0
+56, -2.897777478867742, -0.7764571353055585, 0
+57, -2.598076211355317, -1.499999999996534, 0
+58, -2.12132034356377, -2.121320343555515, 0
+59, -1.500000000003278, -2.598076211351423, 0
+60, -0.7764571353089083, -2.897777478866844, 0
+61, 0.7764571353055585, -2.897777478867742, 0
+62, 1.499999999996534, -2.598076211355317, 0
+63, 2.121320343555515, -2.12132034356377, 0
+64, 2.598076211351423, -1.500000000003278, 0
+65, 2.897777478866844, -0.7764571353089083, 0
+66, -3.444356651487717, -3.623314556893697, 0
+67, 3.630770861619578, -3.450375428811415, 0
+68, -3.606724759555615, 3.47764896615217, 0
+69, 3.477858484632714, 3.688746030709118, 0
+70, -4.156399691567252, 1.82670878905137, 0
+71, -1.828813450471918, -4.158942678708136, 0
+72, 4.161732457494641, -1.826702910276453, 0
+73, 1.833578373586338, 4.152877803135805, 0
+74, 1.82855514341317, -4.134124222527332, 0
+75, -4.137261178922145, -1.824225453057445, 0
+76, -1.816315221240793, 4.139580491425663, 0
+77, 4.137830708436139, 1.865232377094907, 0
+78, -0.8324523395574734, -3.513832842847894, 0
+79, 3.514812307625984, -0.8327980029018447, 0
+80, -3.51115251493793, 0.8364778404694678, 0
+81, 0.8323027666034133, 3.510317215568977, 0
+82, 0.8130561776793406, -3.509461792913007, 0
+83, -0.8040879808394579, 3.508799622305474, 0
+84, 3.507637985249148, 0.8416152255540563, 0
+85, -3.510297467295204, -0.8103454998307921, 0
+86, 2.879356235150938, 2.925058052321703, 0
+87, -2.955541855893706, 2.743965685902447, 0
+88, -2.741685532801538, -2.974876072989343, 0
+89, 2.986198151494071, -2.746872692158193, 0
+90, -4.132617430180748, 2.609629903505891, 0
+91, -2.628677771111532, -4.143741570119716, 0
+92, 4.137613268344874, -2.63176582991825, 0
+93, 2.623548302690499, 4.13012267383317, 0
+94, -4.12928603258286, -2.623527906833591, 0
+95, -2.624220664337432, 4.143597264629516, 0
+96, 4.089730028010475, 2.776574452224664, 0
+97, 2.629652458540163, -4.129049284126983, 0
+98, 3.302367905051891, 1.70968032242428, 0
+99, 1.53952226362465, -3.315374538869128, 0
+100, -3.332608471710656, -1.535150946932129, 0
+101, -1.526301238015451, 3.329296275968781, 0
+102, -3.320587535849678, 1.683096625180183, 0
+103, -1.683526668391519, -3.324593751159378, 0
+104, 3.327091738802665, -1.68393626645891, 0
+105, 1.639192452215204, 3.325378675743715, 0
+106, 3.287248188077074, -4.189087367530157, 0
+107, -3.295778441163215, 4.201117225082506, 0
+108, -4.178468025548098, -3.275823178071892, 0
+109, 4.12726588759978, 3.663811975302485, 0
+110, -4.106859944586758, 3.331114243505222, 0
+111, 3.285661280600918, 4.188431731448394, 0
+112, -3.286453411102075, -4.188960608239536, 0
+113, 4.191730151615054, -3.289023590308119, 0
+114, -0.8835688592767325, 4.235974096384401, 0
+115, 0.9057525013633964, -4.243953044530109, 0
+116, -4.241218968046286, -0.9001784922335702, 0
+117, 4.228781092544913, 0.9229902907771372, 0
+118, -0.9143189641212353, -4.23580907643538, 0
+119, 4.236027863443724, -0.9133192803687695, 0
+120, 0.9149732711747496, 4.23204845910478, 0
+121, -4.232840912800372, 0.9176807031613724, 0
+122, 0.008845214798486375, 4.260893647236393, 0
+123, 4.267986468814929, 0.001017592833179522, 0
+124, -0.002629743415697635, -4.266103757267889, 0
+125, -4.262233825920551, 0.005818638193458033, 0
+126, 3.592635884483748, -2.634007018244112e-05, 0
+127, -0.000940883892022749, -3.594535563634333, 0
+128, -3.577141536751654, 0.005642074623536417, 0
+129, 0.00838864987239782, 3.576052457680655, 0
+*Element, type=T3D2, ELSET=Line1
+10, 1, 10
+11, 10, 11
+12, 11, 12
+13, 12, 13
+14, 13, 14
+15, 14, 15
+16, 15, 16
+17, 16, 17
+18, 17, 18
+19, 18, 2
+*Element, type=T3D2, ELSET=Line2
+20, 2, 19
+21, 19, 20
+22, 20, 21
+23, 21, 22
+24, 22, 23
+25, 23, 24
+26, 24, 25
+27, 25, 26
+28, 26, 27
+29, 27, 3
+*Element, type=T3D2, ELSET=Line3
+30, 3, 28
+31, 28, 29
+32, 29, 30
+33, 30, 31
+34, 31, 32
+35, 32, 33
+36, 33, 34
+37, 34, 35
+38, 35, 36
+39, 36, 4
+*Element, type=T3D2, ELSET=Line4
+40, 4, 37
+41, 37, 38
+42, 38, 39
+43, 39, 40
+44, 40, 41
+45, 41, 42
+46, 42, 43
+47, 43, 44
+48, 44, 45
+49, 45, 1
+*Element, type=T3D2, ELSET=Line5
+50, 8, 46
+51, 46, 47
+52, 47, 48
+53, 48, 49
+54, 49, 50
+55, 50, 7
+*Element, type=T3D2, ELSET=Line6
+56, 7, 51
+57, 51, 52
+58, 52, 53
+59, 53, 54
+60, 54, 55
+61, 55, 9
+*Element, type=T3D2, ELSET=Line7
+62, 9, 56
+63, 56, 57
+64, 57, 58
+65, 58, 59
+66, 59, 60
+67, 60, 6
+*Element, type=T3D2, ELSET=Line8
+68, 6, 61
+69, 61, 62
+70, 62, 63
+71, 63, 64
+72, 64, 65
+73, 65, 8
+*Element, type=CPS4, ELSET=Surface11
+1050, 19, 106, 18, 2
+1051, 28, 109, 27, 3
+1052, 37, 107, 36, 4
+1053, 10, 108, 45, 1
+1058, 53, 52, 101, 87
+1059, 48, 47, 98, 86
+1060, 63, 62, 99, 89
+1061, 58, 57, 100, 88
+1062, 58, 88, 103, 59
+1063, 48, 86, 105, 49
+1064, 53, 87, 102, 54
+1065, 63, 89, 104, 64
+1066, 6, 127, 82, 61
+1067, 8, 126, 84, 46
+1068, 7, 129, 83, 51
+1069, 9, 128, 85, 56
+1070, 91, 71, 103, 88
+1071, 93, 73, 105, 86
+1072, 90, 70, 102, 87
+1073, 92, 72, 104, 89
+1074, 94, 88, 100, 75
+1075, 96, 86, 98, 77
+1076, 95, 87, 101, 76
+1077, 97, 89, 99, 74
+1078, 80, 128, 9, 55
+1079, 81, 129, 7, 50
+1080, 79, 126, 8, 65
+1081, 78, 127, 6, 60
+1082, 121, 40, 41, 125
+1083, 120, 31, 32, 122
+1084, 119, 22, 23, 123
+1085, 118, 13, 14, 124
+1086, 71, 91, 11, 12
+1087, 70, 90, 38, 39
+1088, 72, 92, 20, 21
+1089, 73, 93, 29, 30
+1090, 77, 25, 26, 96
+1091, 75, 43, 44, 94
+1092, 76, 34, 35, 95
+1093, 74, 16, 17, 97
+1094, 104, 72, 119, 79
+1095, 102, 70, 121, 80
+1096, 105, 73, 120, 81
+1097, 103, 71, 118, 78
+1098, 115, 124, 14, 15
+1099, 117, 123, 23, 24
+1100, 116, 125, 41, 42
+1101, 114, 122, 32, 33
+1102, 100, 85, 116, 75
+1103, 99, 82, 115, 74
+1104, 98, 84, 117, 77
+1105, 101, 83, 114, 76
+1106, 75, 116, 42, 43
+1107, 76, 114, 33, 34
+1108, 74, 115, 15, 16
+1109, 77, 117, 24, 25
+1110, 89, 97, 106, 67
+1111, 87, 95, 107, 68
+1112, 88, 94, 108, 66
+1113, 86, 96, 109, 69
+1114, 86, 69, 111, 93
+1115, 89, 67, 113, 92
+1116, 87, 68, 110, 90
+1117, 88, 66, 112, 91
+1118, 72, 21, 22, 119
+1119, 71, 12, 13, 118
+1120, 73, 30, 31, 120
+1121, 70, 39, 40, 121
+1126, 68, 107, 37, 110
+1127, 66, 108, 10, 112
+1128, 67, 106, 19, 113
+1129, 69, 109, 28, 111
+1130, 37, 38, 90, 110
+1131, 10, 11, 91, 112
+1132, 19, 20, 92, 113
+1133, 28, 29, 93, 111
+1134, 18, 106, 97, 17
+1135, 36, 107, 95, 35
+1136, 45, 108, 94, 44
+1137, 27, 109, 96, 26
+1138, 80, 55, 54, 102
+1139, 78, 60, 59, 103
+1140, 79, 65, 64, 104
+1141, 81, 50, 49, 105
+1142, 83, 101, 52, 51
+1143, 84, 98, 47, 46
+1144, 85, 100, 57, 56
+1145, 82, 99, 62, 61
+1146, 120, 122, 129, 81
+1147, 118, 124, 127, 78
+1148, 121, 125, 128, 80
+1149, 119, 123, 126, 79
+1150, 115, 82, 127, 124
+1151, 114, 83, 129, 122
+1152, 117, 84, 126, 123
+1153, 116, 85, 128, 125
diff --git a/example/steps/hole_3d_cubit.inp b/example/steps/hole_3d_cubit.inp
new file mode 100644
index 0000000..b13650a
--- /dev/null
+++ b/example/steps/hole_3d_cubit.inp
@@ -0,0 +1,2664 @@
+*HEADING
+cubit(/Users/lucas/hole_3d_cubit.inp): 07/01/2014: 07:07:17
+version: 14.0
+**
+********************************** P A R T S **********************************
+*PART, NAME=Part-Default
+**
+********************************** N O D E S **********************************
+*NODE, NSET=ALLNODES
+       1,   -3.000000e+00,    3.673940e-16,    5.000000e+00
+       2,   -2.878479e+00,    8.451977e-01,    5.000000e+00
+       3,   -2.523761e+00,    1.621922e+00,    5.000000e+00
+       4,   -1.964582e+00,    2.267249e+00,    5.000000e+00
+       5,   -1.246245e+00,    2.728896e+00,    5.000000e+00
+       6,   -4.269445e-01,    2.969464e+00,    5.000000e+00
+       7,    4.269445e-01,    2.969464e+00,    5.000000e+00
+       8,    1.246245e+00,    2.728896e+00,    5.000000e+00
+       9,    1.964582e+00,    2.267249e+00,    5.000000e+00
+      10,    2.523761e+00,    1.621922e+00,    5.000000e+00
+      11,    2.878479e+00,    8.451977e-01,    5.000000e+00
+      12,    3.000000e+00,    0.000000e+00,    5.000000e+00
+      13,    2.878479e+00,   -8.451977e-01,    5.000000e+00
+      14,    2.523761e+00,   -1.621922e+00,    5.000000e+00
+      15,    1.964582e+00,   -2.267249e+00,    5.000000e+00
+      16,    1.246245e+00,   -2.728896e+00,    5.000000e+00
+      17,    4.269445e-01,   -2.969464e+00,    5.000000e+00
+      18,   -4.269445e-01,   -2.969464e+00,    5.000000e+00
+      19,   -1.246245e+00,   -2.728896e+00,    5.000000e+00
+      20,   -1.964582e+00,   -2.267249e+00,    5.000000e+00
+      21,   -2.523761e+00,   -1.621922e+00,    5.000000e+00
+      22,   -2.878479e+00,   -8.451977e-01,    5.000000e+00
+      23,    5.000000e+00,   -5.000000e+00,    5.000000e+00
+      24,    5.000000e+00,    5.000000e+00,    5.000000e+00
+      25,    5.000000e+00,   -4.166667e+00,    5.000000e+00
+      26,    5.000000e+00,   -3.333333e+00,    5.000000e+00
+      27,    5.000000e+00,   -2.500000e+00,    5.000000e+00
+      28,    5.000000e+00,   -1.666667e+00,    5.000000e+00
+      29,    5.000000e+00,   -8.333333e-01,    5.000000e+00
+      30,    5.000000e+00,    0.000000e+00,    5.000000e+00
+      31,    5.000000e+00,    8.333333e-01,    5.000000e+00
+      32,    5.000000e+00,    1.666667e+00,    5.000000e+00
+      33,    5.000000e+00,    2.500000e+00,    5.000000e+00
+      34,    5.000000e+00,    3.333333e+00,    5.000000e+00
+      35,    5.000000e+00,    4.166667e+00,    5.000000e+00
+      36,   -5.000000e+00,    5.000000e+00,    5.000000e+00
+      37,    4.166667e+00,    5.000000e+00,    5.000000e+00
+      38,    3.333333e+00,    5.000000e+00,    5.000000e+00
+      39,    2.500000e+00,    5.000000e+00,    5.000000e+00
+      40,    1.666667e+00,    5.000000e+00,    5.000000e+00
+      41,    8.333333e-01,    5.000000e+00,    5.000000e+00
+      42,    0.000000e+00,    5.000000e+00,    5.000000e+00
+      43,   -8.333333e-01,    5.000000e+00,    5.000000e+00
+      44,   -1.666667e+00,    5.000000e+00,    5.000000e+00
+      45,   -2.500000e+00,    5.000000e+00,    5.000000e+00
+      46,   -3.333333e+00,    5.000000e+00,    5.000000e+00
+      47,   -4.166667e+00,    5.000000e+00,    5.000000e+00
+      48,   -5.000000e+00,   -5.000000e+00,    5.000000e+00
+      49,   -5.000000e+00,    4.166667e+00,    5.000000e+00
+      50,   -5.000000e+00,    3.333333e+00,    5.000000e+00
+      51,   -5.000000e+00,    2.500000e+00,    5.000000e+00
+      52,   -5.000000e+00,    1.666667e+00,    5.000000e+00
+      53,   -5.000000e+00,    8.333333e-01,    5.000000e+00
+      54,   -5.000000e+00,    0.000000e+00,    5.000000e+00
+      55,   -5.000000e+00,   -8.333333e-01,    5.000000e+00
+      56,   -5.000000e+00,   -1.666667e+00,    5.000000e+00
+      57,   -5.000000e+00,   -2.500000e+00,    5.000000e+00
+      58,   -5.000000e+00,   -3.333333e+00,    5.000000e+00
+      59,   -5.000000e+00,   -4.166667e+00,    5.000000e+00
+      60,   -4.166667e+00,   -5.000000e+00,    5.000000e+00
+      61,   -3.333333e+00,   -5.000000e+00,    5.000000e+00
+      62,   -2.500000e+00,   -5.000000e+00,    5.000000e+00
+      63,   -1.666667e+00,   -5.000000e+00,    5.000000e+00
+      64,   -8.333333e-01,   -5.000000e+00,    5.000000e+00
+      65,    0.000000e+00,   -5.000000e+00,    5.000000e+00
+      66,    8.333333e-01,   -5.000000e+00,    5.000000e+00
+      67,    1.666667e+00,   -5.000000e+00,    5.000000e+00
+      68,    2.500000e+00,   -5.000000e+00,    5.000000e+00
+      69,    3.333333e+00,   -5.000000e+00,    5.000000e+00
+      70,    4.166667e+00,   -5.000000e+00,    5.000000e+00
+      71,    4.080156e+00,   -4.221993e+00,    5.000000e+00
+      72,    3.914282e+00,   -3.394158e+00,    5.000000e+00
+      73,    3.518431e+00,   -2.364823e+00,    5.000000e+00
+      74,    3.816228e+00,   -1.321296e+00,    5.000000e+00
+      75,    3.911426e+00,   -3.828604e-01,    5.000000e+00
+      76,    3.851744e+00,    6.169215e-01,    5.000000e+00
+      77,    4.367777e+00,    1.067366e+00,    5.000000e+00
+      78,    4.164414e+00,    2.588980e+00,    5.000000e+00
+      79,    4.251528e+00,    3.377333e+00,    5.000000e+00
+      80,    4.212534e+00,    4.172338e+00,    5.000000e+00
+      81,   -4.232909e+00,    4.245850e+00,    5.000000e+00
+      82,   -4.232408e+00,    3.535725e+00,    5.000000e+00
+      83,   -4.001597e+00,    2.823007e+00,    5.000000e+00
+      84,   -3.578650e+00,    1.965393e+00,    5.000000e+00
+      85,   -4.001482e+00,   -2.822856e+00,    5.000000e+00
+      86,   -4.229138e+00,   -3.536852e+00,    5.000000e+00
+      87,   -4.228407e+00,   -4.248024e+00,    5.000000e+00
+      88,    3.470223e+00,    4.122143e+00,    5.000000e+00
+      89,    2.713818e+00,    3.926913e+00,    5.000000e+00
+      90,    1.853334e+00,    3.838186e+00,    5.000000e+00
+      91,    1.028238e+00,    3.903866e+00,    5.000000e+00
+      92,    1.782734e-01,    3.943301e+00,    5.000000e+00
+      93,   -2.772073e+00,    4.056834e+00,    5.000000e+00
+      94,   -3.511985e+00,    4.257900e+00,    5.000000e+00
+      95,   -3.509611e+00,   -4.260109e+00,    5.000000e+00
+      96,   -2.773226e+00,   -4.054954e+00,    5.000000e+00
+      97,   -1.801321e+00,   -3.692164e+00,    5.000000e+00
+      98,   -6.983753e-01,   -3.907437e+00,    5.000000e+00
+      99,    2.795061e-01,   -3.912741e+00,    5.000000e+00
+     100,    1.393456e+00,   -3.736053e+00,    5.000000e+00
+     101,    2.163497e+00,   -4.175723e+00,    5.000000e+00
+     102,    2.639438e+00,   -4.508871e+00,    5.000000e+00
+     103,    3.242527e+00,   -4.347026e+00,    5.000000e+00
+     104,   -3.924184e+00,    5.869503e-04,    5.000000e+00
+     105,   -3.858582e+00,    9.162757e-01,    5.000000e+00
+     106,   -2.641377e+00,    2.809676e+00,    5.000000e+00
+     107,   -1.805122e+00,    3.691458e+00,    5.000000e+00
+     108,   -7.250577e-01,    3.895979e+00,    5.000000e+00
+     109,   -3.667918e+00,    3.703972e+00,    5.000000e+00
+     110,   -3.249197e+00,    3.318617e+00,    5.000000e+00
+     111,    3.159382e+00,    2.755193e+00,    5.000000e+00
+     112,    3.447440e+00,    1.687950e+00,    5.000000e+00
+     113,    4.258286e+00,    1.745177e+00,    5.000000e+00
+     114,    2.452949e+00,   -3.007055e+00,    5.000000e+00
+     115,    3.618349e+00,    3.412924e+00,    5.000000e+00
+     116,   -2.646526e+00,   -2.806103e+00,    5.000000e+00
+     117,   -3.577153e+00,   -1.964790e+00,    5.000000e+00
+     118,   -3.854392e+00,   -9.140208e-01,    5.000000e+00
+     119,   -3.250904e+00,   -3.317987e+00,    5.000000e+00
+     120,   -3.663878e+00,   -3.706025e+00,    5.000000e+00
+     121,    2.937419e+00,   -3.705826e+00,    5.000000e+00
+     122,   -3.000000e+00,   -3.673940e-16,   -5.000000e+00
+     123,   -2.878479e+00,   -8.451977e-01,   -5.000000e+00
+     124,   -2.523761e+00,   -1.621922e+00,   -5.000000e+00
+     125,   -1.964582e+00,   -2.267249e+00,   -5.000000e+00
+     126,   -1.246245e+00,   -2.728896e+00,   -5.000000e+00
+     127,   -4.269445e-01,   -2.969464e+00,   -5.000000e+00
+     128,    4.269445e-01,   -2.969464e+00,   -5.000000e+00
+     129,    1.246245e+00,   -2.728896e+00,   -5.000000e+00
+     130,    1.964582e+00,   -2.267249e+00,   -5.000000e+00
+     131,    2.523761e+00,   -1.621922e+00,   -5.000000e+00
+     132,    2.878479e+00,   -8.451977e-01,   -5.000000e+00
+     133,    3.000000e+00,    0.000000e+00,   -5.000000e+00
+     134,    2.878479e+00,    8.451977e-01,   -5.000000e+00
+     135,    2.523761e+00,    1.621922e+00,   -5.000000e+00
+     136,    1.964582e+00,    2.267249e+00,   -5.000000e+00
+     137,    1.246245e+00,    2.728896e+00,   -5.000000e+00
+     138,    4.269445e-01,    2.969464e+00,   -5.000000e+00
+     139,   -4.269445e-01,    2.969464e+00,   -5.000000e+00
+     140,   -1.246245e+00,    2.728896e+00,   -5.000000e+00
+     141,   -1.964582e+00,    2.267249e+00,   -5.000000e+00
+     142,   -2.523761e+00,    1.621922e+00,   -5.000000e+00
+     143,   -2.878479e+00,    8.451977e-01,   -5.000000e+00
+     144,   -3.000000e+00,   -3.673940e-16,   -4.166667e+00
+     145,   -3.000000e+00,   -3.673940e-16,   -3.333333e+00
+     146,   -3.000000e+00,   -3.673940e-16,   -2.500000e+00
+     147,   -3.000000e+00,   -3.673940e-16,   -1.666667e+00
+     148,   -3.000000e+00,   -3.673940e-16,   -8.333333e-01
+     149,   -3.000000e+00,   -3.673940e-16,    0.000000e+00
+     150,   -3.000000e+00,   -3.673940e-16,    8.333333e-01
+     151,   -3.000000e+00,   -3.673940e-16,    1.666667e+00
+     152,   -3.000000e+00,   -3.673940e-16,    2.500000e+00
+     153,   -3.000000e+00,   -3.673940e-16,    3.333333e+00
+     154,   -3.000000e+00,   -3.673940e-16,    4.166667e+00
+     155,   -2.878479e+00,   -8.451977e-01,   -4.166667e+00
+     156,   -2.878479e+00,   -8.451977e-01,   -3.333333e+00
+     157,   -2.878479e+00,   -8.451977e-01,   -2.500000e+00
+     158,   -2.878479e+00,   -8.451977e-01,   -1.666667e+00
+     159,   -2.878479e+00,   -8.451977e-01,   -8.333333e-01
+     160,   -2.878479e+00,   -8.451977e-01,    1.040834e-16
+     161,   -2.878479e+00,   -8.451977e-01,    8.333333e-01
+     162,   -2.878479e+00,   -8.451977e-01,    1.666667e+00
+     163,   -2.878479e+00,   -8.451977e-01,    2.500000e+00
+     164,   -2.878479e+00,   -8.451977e-01,    3.333333e+00
+     165,   -2.878479e+00,   -8.451977e-01,    4.166667e+00
+     166,   -2.523761e+00,   -1.621922e+00,   -4.166667e+00
+     167,   -2.523761e+00,   -1.621922e+00,   -3.333333e+00
+     168,   -2.523761e+00,   -1.621922e+00,   -2.500000e+00
+     169,   -2.523761e+00,   -1.621922e+00,   -1.666667e+00
+     170,   -2.523761e+00,   -1.621922e+00,   -8.333333e-01
+     171,   -2.523761e+00,   -1.621922e+00,   -8.326673e-17
+     172,   -2.523761e+00,   -1.621922e+00,    8.333333e-01
+     173,   -2.523761e+00,   -1.621922e+00,    1.666667e+00
+     174,   -2.523761e+00,   -1.621922e+00,    2.500000e+00
+     175,   -2.523761e+00,   -1.621922e+00,    3.333333e+00
+     176,   -2.523761e+00,   -1.621922e+00,    4.166667e+00
+     177,   -1.964582e+00,   -2.267249e+00,   -4.166667e+00
+     178,   -1.964582e+00,   -2.267249e+00,   -3.333333e+00
+     179,   -1.964582e+00,   -2.267249e+00,   -2.500000e+00
+     180,   -1.964582e+00,   -2.267249e+00,   -1.666667e+00
+     181,   -1.964582e+00,   -2.267249e+00,   -8.333333e-01
+     182,   -1.964582e+00,   -2.267249e+00,    1.665335e-16
+     183,   -1.964582e+00,   -2.267249e+00,    8.333333e-01
+     184,   -1.964582e+00,   -2.267249e+00,    1.666667e+00
+     185,   -1.964582e+00,   -2.267249e+00,    2.500000e+00
+     186,   -1.964582e+00,   -2.267249e+00,    3.333333e+00
+     187,   -1.964582e+00,   -2.267249e+00,    4.166667e+00
+     188,   -1.246245e+00,   -2.728896e+00,   -4.166667e+00
+     189,   -1.246245e+00,   -2.728896e+00,   -3.333333e+00
+     190,   -1.246245e+00,   -2.728896e+00,   -2.500000e+00
+     191,   -1.246245e+00,   -2.728896e+00,   -1.666667e+00
+     192,   -1.246245e+00,   -2.728896e+00,   -8.333333e-01
+     193,   -1.246245e+00,   -2.728896e+00,    1.665335e-16
+     194,   -1.246245e+00,   -2.728896e+00,    8.333333e-01
+     195,   -1.246245e+00,   -2.728896e+00,    1.666667e+00
+     196,   -1.246245e+00,   -2.728896e+00,    2.500000e+00
+     197,   -1.246245e+00,   -2.728896e+00,    3.333333e+00
+     198,   -1.246245e+00,   -2.728896e+00,    4.166667e+00
+     199,   -4.269445e-01,   -2.969464e+00,   -4.166667e+00
+     200,   -4.269445e-01,   -2.969464e+00,   -3.333333e+00
+     201,   -4.269445e-01,   -2.969464e+00,   -2.500000e+00
+     202,   -4.269445e-01,   -2.969464e+00,   -1.666667e+00
+     203,   -4.269445e-01,   -2.969464e+00,   -8.333333e-01
+     204,   -4.269445e-01,   -2.969464e+00,    0.000000e+00
+     205,   -4.269445e-01,   -2.969464e+00,    8.333333e-01
+     206,   -4.269445e-01,   -2.969464e+00,    1.666667e+00
+     207,   -4.269445e-01,   -2.969464e+00,    2.500000e+00
+     208,   -4.269445e-01,   -2.969464e+00,    3.333333e+00
+     209,   -4.269445e-01,   -2.969464e+00,    4.166667e+00
+     210,    4.269445e-01,   -2.969464e+00,   -4.166667e+00
+     211,    4.269445e-01,   -2.969464e+00,   -3.333333e+00
+     212,    4.269445e-01,   -2.969464e+00,   -2.500000e+00
+     213,    4.269445e-01,   -2.969464e+00,   -1.666667e+00
+     214,    4.269445e-01,   -2.969464e+00,   -8.333333e-01
+     215,    4.269445e-01,   -2.969464e+00,    4.163336e-16
+     216,    4.269445e-01,   -2.969464e+00,    8.333333e-01
+     217,    4.269445e-01,   -2.969464e+00,    1.666667e+00
+     218,    4.269445e-01,   -2.969464e+00,    2.500000e+00
+     219,    4.269445e-01,   -2.969464e+00,    3.333333e+00
+     220,    4.269445e-01,   -2.969464e+00,    4.166667e+00
+     221,    1.246245e+00,   -2.728896e+00,   -4.166667e+00
+     222,    1.246245e+00,   -2.728896e+00,   -3.333333e+00
+     223,    1.246245e+00,   -2.728896e+00,   -2.500000e+00
+     224,    1.246245e+00,   -2.728896e+00,   -1.666667e+00
+     225,    1.246245e+00,   -2.728896e+00,   -8.333333e-01
+     226,    1.246245e+00,   -2.728896e+00,    6.661338e-16
+     227,    1.246245e+00,   -2.728896e+00,    8.333333e-01
+     228,    1.246245e+00,   -2.728896e+00,    1.666667e+00
+     229,    1.246245e+00,   -2.728896e+00,    2.500000e+00
+     230,    1.246245e+00,   -2.728896e+00,    3.333333e+00
+     231,    1.246245e+00,   -2.728896e+00,    4.166667e+00
+     232,    1.964582e+00,   -2.267249e+00,   -4.166667e+00
+     233,    1.964582e+00,   -2.267249e+00,   -3.333333e+00
+     234,    1.964582e+00,   -2.267249e+00,   -2.500000e+00
+     235,    1.964582e+00,   -2.267249e+00,   -1.666667e+00
+     236,    1.964582e+00,   -2.267249e+00,   -8.333333e-01
+     237,    1.964582e+00,   -2.267249e+00,    6.661338e-16
+     238,    1.964582e+00,   -2.267249e+00,    8.333333e-01
+     239,    1.964582e+00,   -2.267249e+00,    1.666667e+00
+     240,    1.964582e+00,   -2.267249e+00,    2.500000e+00
+     241,    1.964582e+00,   -2.267249e+00,    3.333333e+00
+     242,    1.964582e+00,   -2.267249e+00,    4.166667e+00
+     243,    2.523761e+00,   -1.621922e+00,   -4.166667e+00
+     244,    2.523761e+00,   -1.621922e+00,   -3.333333e+00
+     245,    2.523761e+00,   -1.621922e+00,   -2.500000e+00
+     246,    2.523761e+00,   -1.621922e+00,   -1.666667e+00
+     247,    2.523761e+00,   -1.621922e+00,   -8.333333e-01
+     248,    2.523761e+00,   -1.621922e+00,   -1.665335e-16
+     249,    2.523761e+00,   -1.621922e+00,    8.333333e-01
+     250,    2.523761e+00,   -1.621922e+00,    1.666667e+00
+     251,    2.523761e+00,   -1.621922e+00,    2.500000e+00
+     252,    2.523761e+00,   -1.621922e+00,    3.333333e+00
+     253,    2.523761e+00,   -1.621922e+00,    4.166667e+00
+     254,    2.878479e+00,   -8.451977e-01,   -4.166667e+00
+     255,    2.878479e+00,   -8.451977e-01,   -3.333333e+00
+     256,    2.878479e+00,   -8.451977e-01,   -2.500000e+00
+     257,    2.878479e+00,   -8.451977e-01,   -1.666667e+00
+     258,    2.878479e+00,   -8.451977e-01,   -8.333333e-01
+     259,    2.878479e+00,   -8.451977e-01,    0.000000e+00
+     260,    2.878479e+00,   -8.451977e-01,    8.333333e-01
+     261,    2.878479e+00,   -8.451977e-01,    1.666667e+00
+     262,    2.878479e+00,   -8.451977e-01,    2.500000e+00
+     263,    2.878479e+00,   -8.451977e-01,    3.333333e+00
+     264,    2.878479e+00,   -8.451977e-01,    4.166667e+00
+     265,    3.000000e+00,    0.000000e+00,   -4.166667e+00
+     266,    3.000000e+00,    0.000000e+00,   -3.333333e+00
+     267,    3.000000e+00,    0.000000e+00,   -2.500000e+00
+     268,    3.000000e+00,    0.000000e+00,   -1.666667e+00
+     269,    3.000000e+00,    0.000000e+00,   -8.333333e-01
+     270,    3.000000e+00,    0.000000e+00,    0.000000e+00
+     271,    3.000000e+00,    0.000000e+00,    8.333333e-01
+     272,    3.000000e+00,    0.000000e+00,    1.666667e+00
+     273,    3.000000e+00,    0.000000e+00,    2.500000e+00
+     274,    3.000000e+00,    0.000000e+00,    3.333333e+00
+     275,    3.000000e+00,    0.000000e+00,    4.166667e+00
+     276,    2.878479e+00,    8.451977e-01,   -4.166667e+00
+     277,    2.878479e+00,    8.451977e-01,   -3.333333e+00
+     278,    2.878479e+00,    8.451977e-01,   -2.500000e+00
+     279,    2.878479e+00,    8.451977e-01,   -1.666667e+00
+     280,    2.878479e+00,    8.451977e-01,   -8.333333e-01
+     281,    2.878479e+00,    8.451977e-01,    4.996004e-16
+     282,    2.878479e+00,    8.451977e-01,    8.333333e-01
+     283,    2.878479e+00,    8.451977e-01,    1.666667e+00
+     284,    2.878479e+00,    8.451977e-01,    2.500000e+00
+     285,    2.878479e+00,    8.451977e-01,    3.333333e+00
+     286,    2.878479e+00,    8.451977e-01,    4.166667e+00
+     287,    2.523761e+00,    1.621922e+00,   -4.166667e+00
+     288,    2.523761e+00,    1.621922e+00,   -3.333333e+00
+     289,    2.523761e+00,    1.621922e+00,   -2.500000e+00
+     290,    2.523761e+00,    1.621922e+00,   -1.666667e+00
+     291,    2.523761e+00,    1.621922e+00,   -8.333333e-01
+     292,    2.523761e+00,    1.621922e+00,    4.996004e-16
+     293,    2.523761e+00,    1.621922e+00,    8.333333e-01
+     294,    2.523761e+00,    1.621922e+00,    1.666667e+00
+     295,    2.523761e+00,    1.621922e+00,    2.500000e+00
+     296,    2.523761e+00,    1.621922e+00,    3.333333e+00
+     297,    2.523761e+00,    1.621922e+00,    4.166667e+00
+     298,    1.964582e+00,    2.267249e+00,   -4.166667e+00
+     299,    1.964582e+00,    2.267249e+00,   -3.333333e+00
+     300,    1.964582e+00,    2.267249e+00,   -2.500000e+00
+     301,    1.964582e+00,    2.267249e+00,   -1.666667e+00
+     302,    1.964582e+00,    2.267249e+00,   -8.333333e-01
+     303,    1.964582e+00,    2.267249e+00,    6.661338e-16
+     304,    1.964582e+00,    2.267249e+00,    8.333333e-01
+     305,    1.964582e+00,    2.267249e+00,    1.666667e+00
+     306,    1.964582e+00,    2.267249e+00,    2.500000e+00
+     307,    1.964582e+00,    2.267249e+00,    3.333333e+00
+     308,    1.964582e+00,    2.267249e+00,    4.166667e+00
+     309,    1.246245e+00,    2.728896e+00,   -4.166667e+00
+     310,    1.246245e+00,    2.728896e+00,   -3.333333e+00
+     311,    1.246245e+00,    2.728896e+00,   -2.500000e+00
+     312,    1.246245e+00,    2.728896e+00,   -1.666667e+00
+     313,    1.246245e+00,    2.728896e+00,   -8.333333e-01
+     314,    1.246245e+00,    2.728896e+00,    6.661338e-16
+     315,    1.246245e+00,    2.728896e+00,    8.333333e-01
+     316,    1.246245e+00,    2.728896e+00,    1.666667e+00
+     317,    1.246245e+00,    2.728896e+00,    2.500000e+00
+     318,    1.246245e+00,    2.728896e+00,    3.333333e+00
+     319,    1.246245e+00,    2.728896e+00,    4.166667e+00
+     320,    4.269445e-01,    2.969464e+00,   -4.166667e+00
+     321,    4.269445e-01,    2.969464e+00,   -3.333333e+00
+     322,    4.269445e-01,    2.969464e+00,   -2.500000e+00
+     323,    4.269445e-01,    2.969464e+00,   -1.666667e+00
+     324,    4.269445e-01,    2.969464e+00,   -8.333333e-01
+     325,    4.269445e-01,    2.969464e+00,    3.330669e-16
+     326,    4.269445e-01,    2.969464e+00,    8.333333e-01
+     327,    4.269445e-01,    2.969464e+00,    1.666667e+00
+     328,    4.269445e-01,    2.969464e+00,    2.500000e+00
+     329,    4.269445e-01,    2.969464e+00,    3.333333e+00
+     330,    4.269445e-01,    2.969464e+00,    4.166667e+00
+     331,   -4.269445e-01,    2.969464e+00,   -4.166667e+00
+     332,   -4.269445e-01,    2.969464e+00,   -3.333333e+00
+     333,   -4.269445e-01,    2.969464e+00,   -2.500000e+00
+     334,   -4.269445e-01,    2.969464e+00,   -1.666667e+00
+     335,   -4.269445e-01,    2.969464e+00,   -8.333333e-01
+     336,   -4.269445e-01,    2.969464e+00,    0.000000e+00
+     337,   -4.269445e-01,    2.969464e+00,    8.333333e-01
+     338,   -4.269445e-01,    2.969464e+00,    1.666667e+00
+     339,   -4.269445e-01,    2.969464e+00,    2.500000e+00
+     340,   -4.269445e-01,    2.969464e+00,    3.333333e+00
+     341,   -4.269445e-01,    2.969464e+00,    4.166667e+00
+     342,   -1.246245e+00,    2.728896e+00,   -4.166667e+00
+     343,   -1.246245e+00,    2.728896e+00,   -3.333333e+00
+     344,   -1.246245e+00,    2.728896e+00,   -2.500000e+00
+     345,   -1.246245e+00,    2.728896e+00,   -1.666667e+00
+     346,   -1.246245e+00,    2.728896e+00,   -8.333333e-01
+     347,   -1.246245e+00,    2.728896e+00,    3.330669e-16
+     348,   -1.246245e+00,    2.728896e+00,    8.333333e-01
+     349,   -1.246245e+00,    2.728896e+00,    1.666667e+00
+     350,   -1.246245e+00,    2.728896e+00,    2.500000e+00
+     351,   -1.246245e+00,    2.728896e+00,    3.333333e+00
+     352,   -1.246245e+00,    2.728896e+00,    4.166667e+00
+     353,   -1.964582e+00,    2.267249e+00,   -4.166667e+00
+     354,   -1.964582e+00,    2.267249e+00,   -3.333333e+00
+     355,   -1.964582e+00,    2.267249e+00,   -2.500000e+00
+     356,   -1.964582e+00,    2.267249e+00,   -1.666667e+00
+     357,   -1.964582e+00,    2.267249e+00,   -8.333333e-01
+     358,   -1.964582e+00,    2.267249e+00,    0.000000e+00
+     359,   -1.964582e+00,    2.267249e+00,    8.333333e-01
+     360,   -1.964582e+00,    2.267249e+00,    1.666667e+00
+     361,   -1.964582e+00,    2.267249e+00,    2.500000e+00
+     362,   -1.964582e+00,    2.267249e+00,    3.333333e+00
+     363,   -1.964582e+00,    2.267249e+00,    4.166667e+00
+     364,   -2.523761e+00,    1.621922e+00,   -4.166667e+00
+     365,   -2.523761e+00,    1.621922e+00,   -3.333333e+00
+     366,   -2.523761e+00,    1.621922e+00,   -2.500000e+00
+     367,   -2.523761e+00,    1.621922e+00,   -1.666667e+00
+     368,   -2.523761e+00,    1.621922e+00,   -8.333333e-01
+     369,   -2.523761e+00,    1.621922e+00,    0.000000e+00
+     370,   -2.523761e+00,    1.621922e+00,    8.333333e-01
+     371,   -2.523761e+00,    1.621922e+00,    1.666667e+00
+     372,   -2.523761e+00,    1.621922e+00,    2.500000e+00
+     373,   -2.523761e+00,    1.621922e+00,    3.333333e+00
+     374,   -2.523761e+00,    1.621922e+00,    4.166667e+00
+     375,   -2.878479e+00,    8.451977e-01,   -4.166667e+00
+     376,   -2.878479e+00,    8.451977e-01,   -3.333333e+00
+     377,   -2.878479e+00,    8.451977e-01,   -2.500000e+00
+     378,   -2.878479e+00,    8.451977e-01,   -1.666667e+00
+     379,   -2.878479e+00,    8.451977e-01,   -8.333333e-01
+     380,   -2.878479e+00,    8.451977e-01,    0.000000e+00
+     381,   -2.878479e+00,    8.451977e-01,    8.333333e-01
+     382,   -2.878479e+00,    8.451977e-01,    1.666667e+00
+     383,   -2.878479e+00,    8.451977e-01,    2.500000e+00
+     384,   -2.878479e+00,    8.451977e-01,    3.333333e+00
+     385,   -2.878479e+00,    8.451977e-01,    4.166667e+00
+     386,   -5.000000e+00,   -5.000000e+00,   -5.000000e+00
+     387,   -5.000000e+00,   -5.000000e+00,    4.166667e+00
+     388,   -5.000000e+00,   -5.000000e+00,    3.333333e+00
+     389,   -5.000000e+00,   -5.000000e+00,    2.500000e+00
+     390,   -5.000000e+00,   -5.000000e+00,    1.666667e+00
+     391,   -5.000000e+00,   -5.000000e+00,    8.333333e-01
+     392,   -5.000000e+00,   -5.000000e+00,    0.000000e+00
+     393,   -5.000000e+00,   -5.000000e+00,   -8.333333e-01
+     394,   -5.000000e+00,   -5.000000e+00,   -1.666667e+00
+     395,   -5.000000e+00,   -5.000000e+00,   -2.500000e+00
+     396,   -5.000000e+00,   -5.000000e+00,   -3.333333e+00
+     397,   -5.000000e+00,   -5.000000e+00,   -4.166667e+00
+     398,    5.000000e+00,   -5.000000e+00,   -5.000000e+00
+     399,    4.166667e+00,   -5.000000e+00,   -5.000000e+00
+     400,    3.333333e+00,   -5.000000e+00,   -5.000000e+00
+     401,    2.500000e+00,   -5.000000e+00,   -5.000000e+00
+     402,    1.666667e+00,   -5.000000e+00,   -5.000000e+00
+     403,    8.333333e-01,   -5.000000e+00,   -5.000000e+00
+     404,    0.000000e+00,   -5.000000e+00,   -5.000000e+00
+     405,   -8.333333e-01,   -5.000000e+00,   -5.000000e+00
+     406,   -1.666667e+00,   -5.000000e+00,   -5.000000e+00
+     407,   -2.500000e+00,   -5.000000e+00,   -5.000000e+00
+     408,   -3.333333e+00,   -5.000000e+00,   -5.000000e+00
+     409,   -4.166667e+00,   -5.000000e+00,   -5.000000e+00
+     410,    5.000000e+00,   -5.000000e+00,    4.166667e+00
+     411,    5.000000e+00,   -5.000000e+00,    3.333333e+00
+     412,    5.000000e+00,   -5.000000e+00,    2.500000e+00
+     413,    5.000000e+00,   -5.000000e+00,    1.666667e+00
+     414,    5.000000e+00,   -5.000000e+00,    8.333333e-01
+     415,    5.000000e+00,   -5.000000e+00,    0.000000e+00
+     416,    5.000000e+00,   -5.000000e+00,   -8.333333e-01
+     417,    5.000000e+00,   -5.000000e+00,   -1.666667e+00
+     418,    5.000000e+00,   -5.000000e+00,   -2.500000e+00
+     419,    5.000000e+00,   -5.000000e+00,   -3.333333e+00
+     420,    5.000000e+00,   -5.000000e+00,   -4.166667e+00
+     421,   -4.166667e+00,   -5.000000e+00,    4.166667e+00
+     422,   -4.166667e+00,   -5.000000e+00,    3.333333e+00
+     423,   -4.166667e+00,   -5.000000e+00,    2.500000e+00
+     424,   -4.166667e+00,   -5.000000e+00,    1.666667e+00
+     425,   -4.166667e+00,   -5.000000e+00,    8.333333e-01
+     426,   -4.166667e+00,   -5.000000e+00,    1.110223e-16
+     427,   -4.166667e+00,   -5.000000e+00,   -8.333333e-01
+     428,   -4.166667e+00,   -5.000000e+00,   -1.666667e+00
+     429,   -4.166667e+00,   -5.000000e+00,   -2.500000e+00
+     430,   -4.166667e+00,   -5.000000e+00,   -3.333333e+00
+     431,   -4.166667e+00,   -5.000000e+00,   -4.166667e+00
+     432,   -3.333333e+00,   -5.000000e+00,    4.166667e+00
+     433,   -3.333333e+00,   -5.000000e+00,    3.333333e+00
+     434,   -3.333333e+00,   -5.000000e+00,    2.500000e+00
+     435,   -3.333333e+00,   -5.000000e+00,    1.666667e+00
+     436,   -3.333333e+00,   -5.000000e+00,    8.333333e-01
+     437,   -3.333333e+00,   -5.000000e+00,    5.551115e-17
+     438,   -3.333333e+00,   -5.000000e+00,   -8.333333e-01
+     439,   -3.333333e+00,   -5.000000e+00,   -1.666667e+00
+     440,   -3.333333e+00,   -5.000000e+00,   -2.500000e+00
+     441,   -3.333333e+00,   -5.000000e+00,   -3.333333e+00
+     442,   -3.333333e+00,   -5.000000e+00,   -4.166667e+00
+     443,   -2.500000e+00,   -5.000000e+00,    4.166667e+00
+     444,   -2.500000e+00,   -5.000000e+00,    3.333333e+00
+     445,   -2.500000e+00,   -5.000000e+00,    2.500000e+00
+     446,   -2.500000e+00,   -5.000000e+00,    1.666667e+00
+     447,   -2.500000e+00,   -5.000000e+00,    8.333333e-01
+     448,   -2.500000e+00,   -5.000000e+00,    0.000000e+00
+     449,   -2.500000e+00,   -5.000000e+00,   -8.333333e-01
+     450,   -2.500000e+00,   -5.000000e+00,   -1.666667e+00
+     451,   -2.500000e+00,   -5.000000e+00,   -2.500000e+00
+     452,   -2.500000e+00,   -5.000000e+00,   -3.333333e+00
+     453,   -2.500000e+00,   -5.000000e+00,   -4.166667e+00
+     454,   -1.666667e+00,   -5.000000e+00,    4.166667e+00
+     455,   -1.666667e+00,   -5.000000e+00,    3.333333e+00
+     456,   -1.666667e+00,   -5.000000e+00,    2.500000e+00
+     457,   -1.666667e+00,   -5.000000e+00,    1.666667e+00
+     458,   -1.666667e+00,   -5.000000e+00,    8.333333e-01
+     459,   -1.666667e+00,   -5.000000e+00,    0.000000e+00
+     460,   -1.666667e+00,   -5.000000e+00,   -8.333333e-01
+     461,   -1.666667e+00,   -5.000000e+00,   -1.666667e+00
+     462,   -1.666667e+00,   -5.000000e+00,   -2.500000e+00
+     463,   -1.666667e+00,   -5.000000e+00,   -3.333333e+00
+     464,   -1.666667e+00,   -5.000000e+00,   -4.166667e+00
+     465,   -8.333333e-01,   -5.000000e+00,    4.166667e+00
+     466,   -8.333333e-01,   -5.000000e+00,    3.333333e+00
+     467,   -8.333333e-01,   -5.000000e+00,    2.500000e+00
+     468,   -8.333333e-01,   -5.000000e+00,    1.666667e+00
+     469,   -8.333333e-01,   -5.000000e+00,    8.333333e-01
+     470,   -8.333333e-01,   -5.000000e+00,    0.000000e+00
+     471,   -8.333333e-01,   -5.000000e+00,   -8.333333e-01
+     472,   -8.333333e-01,   -5.000000e+00,   -1.666667e+00
+     473,   -8.333333e-01,   -5.000000e+00,   -2.500000e+00
+     474,   -8.333333e-01,   -5.000000e+00,   -3.333333e+00
+     475,   -8.333333e-01,   -5.000000e+00,   -4.166667e+00
+     476,    0.000000e+00,   -5.000000e+00,    4.166667e+00
+     477,    0.000000e+00,   -5.000000e+00,    3.333333e+00
+     478,    0.000000e+00,   -5.000000e+00,    2.500000e+00
+     479,    0.000000e+00,   -5.000000e+00,    1.666667e+00
+     480,    0.000000e+00,   -5.000000e+00,    8.333333e-01
+     481,    0.000000e+00,   -5.000000e+00,    0.000000e+00
+     482,    0.000000e+00,   -5.000000e+00,   -8.333333e-01
+     483,    0.000000e+00,   -5.000000e+00,   -1.666667e+00
+     484,    0.000000e+00,   -5.000000e+00,   -2.500000e+00
+     485,    0.000000e+00,   -5.000000e+00,   -3.333333e+00
+     486,    0.000000e+00,   -5.000000e+00,   -4.166667e+00
+     487,    8.333333e-01,   -5.000000e+00,    4.166667e+00
+     488,    8.333333e-01,   -5.000000e+00,    3.333333e+00
+     489,    8.333333e-01,   -5.000000e+00,    2.500000e+00
+     490,    8.333333e-01,   -5.000000e+00,    1.666667e+00
+     491,    8.333333e-01,   -5.000000e+00,    8.333333e-01
+     492,    8.333333e-01,   -5.000000e+00,    0.000000e+00
+     493,    8.333333e-01,   -5.000000e+00,   -8.333333e-01
+     494,    8.333333e-01,   -5.000000e+00,   -1.666667e+00
+     495,    8.333333e-01,   -5.000000e+00,   -2.500000e+00
+     496,    8.333333e-01,   -5.000000e+00,   -3.333333e+00
+     497,    8.333333e-01,   -5.000000e+00,   -4.166667e+00
+     498,    1.666667e+00,   -5.000000e+00,    4.166667e+00
+     499,    1.666667e+00,   -5.000000e+00,    3.333333e+00
+     500,    1.666667e+00,   -5.000000e+00,    2.500000e+00
+     501,    1.666667e+00,   -5.000000e+00,    1.666667e+00
+     502,    1.666667e+00,   -5.000000e+00,    8.333333e-01
+     503,    1.666667e+00,   -5.000000e+00,    0.000000e+00
+     504,    1.666667e+00,   -5.000000e+00,   -8.333333e-01
+     505,    1.666667e+00,   -5.000000e+00,   -1.666667e+00
+     506,    1.666667e+00,   -5.000000e+00,   -2.500000e+00
+     507,    1.666667e+00,   -5.000000e+00,   -3.333333e+00
+     508,    1.666667e+00,   -5.000000e+00,   -4.166667e+00
+     509,    2.500000e+00,   -5.000000e+00,    4.166667e+00
+     510,    2.500000e+00,   -5.000000e+00,    3.333333e+00
+     511,    2.500000e+00,   -5.000000e+00,    2.500000e+00
+     512,    2.500000e+00,   -5.000000e+00,    1.666667e+00
+     513,    2.500000e+00,   -5.000000e+00,    8.333333e-01
+     514,    2.500000e+00,   -5.000000e+00,    0.000000e+00
+     515,    2.500000e+00,   -5.000000e+00,   -8.333333e-01
+     516,    2.500000e+00,   -5.000000e+00,   -1.666667e+00
+     517,    2.500000e+00,   -5.000000e+00,   -2.500000e+00
+     518,    2.500000e+00,   -5.000000e+00,   -3.333333e+00
+     519,    2.500000e+00,   -5.000000e+00,   -4.166667e+00
+     520,    3.333333e+00,   -5.000000e+00,    4.166667e+00
+     521,    3.333333e+00,   -5.000000e+00,    3.333333e+00
+     522,    3.333333e+00,   -5.000000e+00,    2.500000e+00
+     523,    3.333333e+00,   -5.000000e+00,    1.666667e+00
+     524,    3.333333e+00,   -5.000000e+00,    8.333333e-01
+     525,    3.333333e+00,   -5.000000e+00,    0.000000e+00
+     526,    3.333333e+00,   -5.000000e+00,   -8.333333e-01
+     527,    3.333333e+00,   -5.000000e+00,   -1.666667e+00
+     528,    3.333333e+00,   -5.000000e+00,   -2.500000e+00
+     529,    3.333333e+00,   -5.000000e+00,   -3.333333e+00
+     530,    3.333333e+00,   -5.000000e+00,   -4.166667e+00
+     531,    4.166667e+00,   -5.000000e+00,    4.166667e+00
+     532,    4.166667e+00,   -5.000000e+00,    3.333333e+00
+     533,    4.166667e+00,   -5.000000e+00,    2.500000e+00
+     534,    4.166667e+00,   -5.000000e+00,    1.666667e+00
+     535,    4.166667e+00,   -5.000000e+00,    8.333333e-01
+     536,    4.166667e+00,   -5.000000e+00,    0.000000e+00
+     537,    4.166667e+00,   -5.000000e+00,   -8.333333e-01
+     538,    4.166667e+00,   -5.000000e+00,   -1.666667e+00
+     539,    4.166667e+00,   -5.000000e+00,   -2.500000e+00
+     540,    4.166667e+00,   -5.000000e+00,   -3.333333e+00
+     541,    4.166667e+00,   -5.000000e+00,   -4.166667e+00
+     542,   -5.000000e+00,    5.000000e+00,   -5.000000e+00
+     543,   -5.000000e+00,    5.000000e+00,    4.166667e+00
+     544,   -5.000000e+00,    5.000000e+00,    3.333333e+00
+     545,   -5.000000e+00,    5.000000e+00,    2.500000e+00
+     546,   -5.000000e+00,    5.000000e+00,    1.666667e+00
+     547,   -5.000000e+00,    5.000000e+00,    8.333333e-01
+     548,   -5.000000e+00,    5.000000e+00,    0.000000e+00
+     549,   -5.000000e+00,    5.000000e+00,   -8.333333e-01
+     550,   -5.000000e+00,    5.000000e+00,   -1.666667e+00
+     551,   -5.000000e+00,    5.000000e+00,   -2.500000e+00
+     552,   -5.000000e+00,    5.000000e+00,   -3.333333e+00
+     553,   -5.000000e+00,    5.000000e+00,   -4.166667e+00
+     554,   -5.000000e+00,   -4.166667e+00,   -5.000000e+00
+     555,   -5.000000e+00,   -3.333333e+00,   -5.000000e+00
+     556,   -5.000000e+00,   -2.500000e+00,   -5.000000e+00
+     557,   -5.000000e+00,   -1.666667e+00,   -5.000000e+00
+     558,   -5.000000e+00,   -8.333333e-01,   -5.000000e+00
+     559,   -5.000000e+00,    0.000000e+00,   -5.000000e+00
+     560,   -5.000000e+00,    8.333333e-01,   -5.000000e+00
+     561,   -5.000000e+00,    1.666667e+00,   -5.000000e+00
+     562,   -5.000000e+00,    2.500000e+00,   -5.000000e+00
+     563,   -5.000000e+00,    3.333333e+00,   -5.000000e+00
+     564,   -5.000000e+00,    4.166667e+00,   -5.000000e+00
+     565,   -5.000000e+00,    4.166667e+00,    4.166667e+00
+     566,   -5.000000e+00,    4.166667e+00,    3.333333e+00
+     567,   -5.000000e+00,    4.166667e+00,    2.500000e+00
+     568,   -5.000000e+00,    4.166667e+00,    1.666667e+00
+     569,   -5.000000e+00,    4.166667e+00,    8.333333e-01
+     570,   -5.000000e+00,    4.166667e+00,    1.110223e-16
+     571,   -5.000000e+00,    4.166667e+00,   -8.333333e-01
+     572,   -5.000000e+00,    4.166667e+00,   -1.666667e+00
+     573,   -5.000000e+00,    4.166667e+00,   -2.500000e+00
+     574,   -5.000000e+00,    4.166667e+00,   -3.333333e+00
+     575,   -5.000000e+00,    4.166667e+00,   -4.166667e+00
+     576,   -5.000000e+00,    3.333333e+00,    4.166667e+00
+     577,   -5.000000e+00,    3.333333e+00,    3.333333e+00
+     578,   -5.000000e+00,    3.333333e+00,    2.500000e+00
+     579,   -5.000000e+00,    3.333333e+00,    1.666667e+00
+     580,   -5.000000e+00,    3.333333e+00,    8.333333e-01
+     581,   -5.000000e+00,    3.333333e+00,    5.551115e-17
+     582,   -5.000000e+00,    3.333333e+00,   -8.333333e-01
+     583,   -5.000000e+00,    3.333333e+00,   -1.666667e+00
+     584,   -5.000000e+00,    3.333333e+00,   -2.500000e+00
+     585,   -5.000000e+00,    3.333333e+00,   -3.333333e+00
+     586,   -5.000000e+00,    3.333333e+00,   -4.166667e+00
+     587,   -5.000000e+00,    2.500000e+00,    4.166667e+00
+     588,   -5.000000e+00,    2.500000e+00,    3.333333e+00
+     589,   -5.000000e+00,    2.500000e+00,    2.500000e+00
+     590,   -5.000000e+00,    2.500000e+00,    1.666667e+00
+     591,   -5.000000e+00,    2.500000e+00,    8.333333e-01
+     592,   -5.000000e+00,    2.500000e+00,    0.000000e+00
+     593,   -5.000000e+00,    2.500000e+00,   -8.333333e-01
+     594,   -5.000000e+00,    2.500000e+00,   -1.666667e+00
+     595,   -5.000000e+00,    2.500000e+00,   -2.500000e+00
+     596,   -5.000000e+00,    2.500000e+00,   -3.333333e+00
+     597,   -5.000000e+00,    2.500000e+00,   -4.166667e+00
+     598,   -5.000000e+00,    1.666667e+00,    4.166667e+00
+     599,   -5.000000e+00,    1.666667e+00,    3.333333e+00
+     600,   -5.000000e+00,    1.666667e+00,    2.500000e+00
+     601,   -5.000000e+00,    1.666667e+00,    1.666667e+00
+     602,   -5.000000e+00,    1.666667e+00,    8.333333e-01
+     603,   -5.000000e+00,    1.666667e+00,    0.000000e+00
+     604,   -5.000000e+00,    1.666667e+00,   -8.333333e-01
+     605,   -5.000000e+00,    1.666667e+00,   -1.666667e+00
+     606,   -5.000000e+00,    1.666667e+00,   -2.500000e+00
+     607,   -5.000000e+00,    1.666667e+00,   -3.333333e+00
+     608,   -5.000000e+00,    1.666667e+00,   -4.166667e+00
+     609,   -5.000000e+00,    8.333333e-01,    4.166667e+00
+     610,   -5.000000e+00,    8.333333e-01,    3.333333e+00
+     611,   -5.000000e+00,    8.333333e-01,    2.500000e+00
+     612,   -5.000000e+00,    8.333333e-01,    1.666667e+00
+     613,   -5.000000e+00,    8.333333e-01,    8.333333e-01
+     614,   -5.000000e+00,    8.333333e-01,    0.000000e+00
+     615,   -5.000000e+00,    8.333333e-01,   -8.333333e-01
+     616,   -5.000000e+00,    8.333333e-01,   -1.666667e+00
+     617,   -5.000000e+00,    8.333333e-01,   -2.500000e+00
+     618,   -5.000000e+00,    8.333333e-01,   -3.333333e+00
+     619,   -5.000000e+00,    8.333333e-01,   -4.166667e+00
+     620,   -5.000000e+00,    0.000000e+00,    4.166667e+00
+     621,   -5.000000e+00,    0.000000e+00,    3.333333e+00
+     622,   -5.000000e+00,    0.000000e+00,    2.500000e+00
+     623,   -5.000000e+00,    0.000000e+00,    1.666667e+00
+     624,   -5.000000e+00,    0.000000e+00,    8.333333e-01
+     625,   -5.000000e+00,    0.000000e+00,    0.000000e+00
+     626,   -5.000000e+00,    0.000000e+00,   -8.333333e-01
+     627,   -5.000000e+00,    0.000000e+00,   -1.666667e+00
+     628,   -5.000000e+00,    0.000000e+00,   -2.500000e+00
+     629,   -5.000000e+00,    0.000000e+00,   -3.333333e+00
+     630,   -5.000000e+00,    0.000000e+00,   -4.166667e+00
+     631,   -5.000000e+00,   -8.333333e-01,    4.166667e+00
+     632,   -5.000000e+00,   -8.333333e-01,    3.333333e+00
+     633,   -5.000000e+00,   -8.333333e-01,    2.500000e+00
+     634,   -5.000000e+00,   -8.333333e-01,    1.666667e+00
+     635,   -5.000000e+00,   -8.333333e-01,    8.333333e-01
+     636,   -5.000000e+00,   -8.333333e-01,    0.000000e+00
+     637,   -5.000000e+00,   -8.333333e-01,   -8.333333e-01
+     638,   -5.000000e+00,   -8.333333e-01,   -1.666667e+00
+     639,   -5.000000e+00,   -8.333333e-01,   -2.500000e+00
+     640,   -5.000000e+00,   -8.333333e-01,   -3.333333e+00
+     641,   -5.000000e+00,   -8.333333e-01,   -4.166667e+00
+     642,   -5.000000e+00,   -1.666667e+00,    4.166667e+00
+     643,   -5.000000e+00,   -1.666667e+00,    3.333333e+00
+     644,   -5.000000e+00,   -1.666667e+00,    2.500000e+00
+     645,   -5.000000e+00,   -1.666667e+00,    1.666667e+00
+     646,   -5.000000e+00,   -1.666667e+00,    8.333333e-01
+     647,   -5.000000e+00,   -1.666667e+00,    0.000000e+00
+     648,   -5.000000e+00,   -1.666667e+00,   -8.333333e-01
+     649,   -5.000000e+00,   -1.666667e+00,   -1.666667e+00
+     650,   -5.000000e+00,   -1.666667e+00,   -2.500000e+00
+     651,   -5.000000e+00,   -1.666667e+00,   -3.333333e+00
+     652,   -5.000000e+00,   -1.666667e+00,   -4.166667e+00
+     653,   -5.000000e+00,   -2.500000e+00,    4.166667e+00
+     654,   -5.000000e+00,   -2.500000e+00,    3.333333e+00
+     655,   -5.000000e+00,   -2.500000e+00,    2.500000e+00
+     656,   -5.000000e+00,   -2.500000e+00,    1.666667e+00
+     657,   -5.000000e+00,   -2.500000e+00,    8.333333e-01
+     658,   -5.000000e+00,   -2.500000e+00,    0.000000e+00
+     659,   -5.000000e+00,   -2.500000e+00,   -8.333333e-01
+     660,   -5.000000e+00,   -2.500000e+00,   -1.666667e+00
+     661,   -5.000000e+00,   -2.500000e+00,   -2.500000e+00
+     662,   -5.000000e+00,   -2.500000e+00,   -3.333333e+00
+     663,   -5.000000e+00,   -2.500000e+00,   -4.166667e+00
+     664,   -5.000000e+00,   -3.333333e+00,    4.166667e+00
+     665,   -5.000000e+00,   -3.333333e+00,    3.333333e+00
+     666,   -5.000000e+00,   -3.333333e+00,    2.500000e+00
+     667,   -5.000000e+00,   -3.333333e+00,    1.666667e+00
+     668,   -5.000000e+00,   -3.333333e+00,    8.333333e-01
+     669,   -5.000000e+00,   -3.333333e+00,    0.000000e+00
+     670,   -5.000000e+00,   -3.333333e+00,   -8.333333e-01
+     671,   -5.000000e+00,   -3.333333e+00,   -1.666667e+00
+     672,   -5.000000e+00,   -3.333333e+00,   -2.500000e+00
+     673,   -5.000000e+00,   -3.333333e+00,   -3.333333e+00
+     674,   -5.000000e+00,   -3.333333e+00,   -4.166667e+00
+     675,   -5.000000e+00,   -4.166667e+00,    4.166667e+00
+     676,   -5.000000e+00,   -4.166667e+00,    3.333333e+00
+     677,   -5.000000e+00,   -4.166667e+00,    2.500000e+00
+     678,   -5.000000e+00,   -4.166667e+00,    1.666667e+00
+     679,   -5.000000e+00,   -4.166667e+00,    8.333333e-01
+     680,   -5.000000e+00,   -4.166667e+00,    0.000000e+00
+     681,   -5.000000e+00,   -4.166667e+00,   -8.333333e-01
+     682,   -5.000000e+00,   -4.166667e+00,   -1.666667e+00
+     683,   -5.000000e+00,   -4.166667e+00,   -2.500000e+00
+     684,   -5.000000e+00,   -4.166667e+00,   -3.333333e+00
+     685,   -5.000000e+00,   -4.166667e+00,   -4.166667e+00
+     686,    5.000000e+00,    5.000000e+00,   -5.000000e+00
+     687,    5.000000e+00,    5.000000e+00,    4.166667e+00
+     688,    5.000000e+00,    5.000000e+00,    3.333333e+00
+     689,    5.000000e+00,    5.000000e+00,    2.500000e+00
+     690,    5.000000e+00,    5.000000e+00,    1.666667e+00
+     691,    5.000000e+00,    5.000000e+00,    8.333333e-01
+     692,    5.000000e+00,    5.000000e+00,    0.000000e+00
+     693,    5.000000e+00,    5.000000e+00,   -8.333333e-01
+     694,    5.000000e+00,    5.000000e+00,   -1.666667e+00
+     695,    5.000000e+00,    5.000000e+00,   -2.500000e+00
+     696,    5.000000e+00,    5.000000e+00,   -3.333333e+00
+     697,    5.000000e+00,    5.000000e+00,   -4.166667e+00
+     698,   -4.166667e+00,    5.000000e+00,   -5.000000e+00
+     699,   -3.333333e+00,    5.000000e+00,   -5.000000e+00
+     700,   -2.500000e+00,    5.000000e+00,   -5.000000e+00
+     701,   -1.666667e+00,    5.000000e+00,   -5.000000e+00
+     702,   -8.333333e-01,    5.000000e+00,   -5.000000e+00
+     703,    0.000000e+00,    5.000000e+00,   -5.000000e+00
+     704,    8.333333e-01,    5.000000e+00,   -5.000000e+00
+     705,    1.666667e+00,    5.000000e+00,   -5.000000e+00
+     706,    2.500000e+00,    5.000000e+00,   -5.000000e+00
+     707,    3.333333e+00,    5.000000e+00,   -5.000000e+00
+     708,    4.166667e+00,    5.000000e+00,   -5.000000e+00
+     709,    4.166667e+00,    5.000000e+00,    4.166667e+00
+     710,    4.166667e+00,    5.000000e+00,    3.333333e+00
+     711,    4.166667e+00,    5.000000e+00,    2.500000e+00
+     712,    4.166667e+00,    5.000000e+00,    1.666667e+00
+     713,    4.166667e+00,    5.000000e+00,    8.333333e-01
+     714,    4.166667e+00,    5.000000e+00,    1.110223e-16
+     715,    4.166667e+00,    5.000000e+00,   -8.333333e-01
+     716,    4.166667e+00,    5.000000e+00,   -1.666667e+00
+     717,    4.166667e+00,    5.000000e+00,   -2.500000e+00
+     718,    4.166667e+00,    5.000000e+00,   -3.333333e+00
+     719,    4.166667e+00,    5.000000e+00,   -4.166667e+00
+     720,    3.333333e+00,    5.000000e+00,    4.166667e+00
+     721,    3.333333e+00,    5.000000e+00,    3.333333e+00
+     722,    3.333333e+00,    5.000000e+00,    2.500000e+00
+     723,    3.333333e+00,    5.000000e+00,    1.666667e+00
+     724,    3.333333e+00,    5.000000e+00,    8.333333e-01
+     725,    3.333333e+00,    5.000000e+00,    5.551115e-17
+     726,    3.333333e+00,    5.000000e+00,   -8.333333e-01
+     727,    3.333333e+00,    5.000000e+00,   -1.666667e+00
+     728,    3.333333e+00,    5.000000e+00,   -2.500000e+00
+     729,    3.333333e+00,    5.000000e+00,   -3.333333e+00
+     730,    3.333333e+00,    5.000000e+00,   -4.166667e+00
+     731,    2.500000e+00,    5.000000e+00,    4.166667e+00
+     732,    2.500000e+00,    5.000000e+00,    3.333333e+00
+     733,    2.500000e+00,    5.000000e+00,    2.500000e+00
+     734,    2.500000e+00,    5.000000e+00,    1.666667e+00
+     735,    2.500000e+00,    5.000000e+00,    8.333333e-01
+     736,    2.500000e+00,    5.000000e+00,    0.000000e+00
+     737,    2.500000e+00,    5.000000e+00,   -8.333333e-01
+     738,    2.500000e+00,    5.000000e+00,   -1.666667e+00
+     739,    2.500000e+00,    5.000000e+00,   -2.500000e+00
+     740,    2.500000e+00,    5.000000e+00,   -3.333333e+00
+     741,    2.500000e+00,    5.000000e+00,   -4.166667e+00
+     742,    1.666667e+00,    5.000000e+00,    4.166667e+00
+     743,    1.666667e+00,    5.000000e+00,    3.333333e+00
+     744,    1.666667e+00,    5.000000e+00,    2.500000e+00
+     745,    1.666667e+00,    5.000000e+00,    1.666667e+00
+     746,    1.666667e+00,    5.000000e+00,    8.333333e-01
+     747,    1.666667e+00,    5.000000e+00,    0.000000e+00
+     748,    1.666667e+00,    5.000000e+00,   -8.333333e-01
+     749,    1.666667e+00,    5.000000e+00,   -1.666667e+00
+     750,    1.666667e+00,    5.000000e+00,   -2.500000e+00
+     751,    1.666667e+00,    5.000000e+00,   -3.333333e+00
+     752,    1.666667e+00,    5.000000e+00,   -4.166667e+00
+     753,    8.333333e-01,    5.000000e+00,    4.166667e+00
+     754,    8.333333e-01,    5.000000e+00,    3.333333e+00
+     755,    8.333333e-01,    5.000000e+00,    2.500000e+00
+     756,    8.333333e-01,    5.000000e+00,    1.666667e+00
+     757,    8.333333e-01,    5.000000e+00,    8.333333e-01
+     758,    8.333333e-01,    5.000000e+00,    0.000000e+00
+     759,    8.333333e-01,    5.000000e+00,   -8.333333e-01
+     760,    8.333333e-01,    5.000000e+00,   -1.666667e+00
+     761,    8.333333e-01,    5.000000e+00,   -2.500000e+00
+     762,    8.333333e-01,    5.000000e+00,   -3.333333e+00
+     763,    8.333333e-01,    5.000000e+00,   -4.166667e+00
+     764,    0.000000e+00,    5.000000e+00,    4.166667e+00
+     765,    0.000000e+00,    5.000000e+00,    3.333333e+00
+     766,    0.000000e+00,    5.000000e+00,    2.500000e+00
+     767,    0.000000e+00,    5.000000e+00,    1.666667e+00
+     768,    0.000000e+00,    5.000000e+00,    8.333333e-01
+     769,    0.000000e+00,    5.000000e+00,    0.000000e+00
+     770,    0.000000e+00,    5.000000e+00,   -8.333333e-01
+     771,    0.000000e+00,    5.000000e+00,   -1.666667e+00
+     772,    0.000000e+00,    5.000000e+00,   -2.500000e+00
+     773,    0.000000e+00,    5.000000e+00,   -3.333333e+00
+     774,    0.000000e+00,    5.000000e+00,   -4.166667e+00
+     775,   -8.333333e-01,    5.000000e+00,    4.166667e+00
+     776,   -8.333333e-01,    5.000000e+00,    3.333333e+00
+     777,   -8.333333e-01,    5.000000e+00,    2.500000e+00
+     778,   -8.333333e-01,    5.000000e+00,    1.666667e+00
+     779,   -8.333333e-01,    5.000000e+00,    8.333333e-01
+     780,   -8.333333e-01,    5.000000e+00,    0.000000e+00
+     781,   -8.333333e-01,    5.000000e+00,   -8.333333e-01
+     782,   -8.333333e-01,    5.000000e+00,   -1.666667e+00
+     783,   -8.333333e-01,    5.000000e+00,   -2.500000e+00
+     784,   -8.333333e-01,    5.000000e+00,   -3.333333e+00
+     785,   -8.333333e-01,    5.000000e+00,   -4.166667e+00
+     786,   -1.666667e+00,    5.000000e+00,    4.166667e+00
+     787,   -1.666667e+00,    5.000000e+00,    3.333333e+00
+     788,   -1.666667e+00,    5.000000e+00,    2.500000e+00
+     789,   -1.666667e+00,    5.000000e+00,    1.666667e+00
+     790,   -1.666667e+00,    5.000000e+00,    8.333333e-01
+     791,   -1.666667e+00,    5.000000e+00,    0.000000e+00
+     792,   -1.666667e+00,    5.000000e+00,   -8.333333e-01
+     793,   -1.666667e+00,    5.000000e+00,   -1.666667e+00
+     794,   -1.666667e+00,    5.000000e+00,   -2.500000e+00
+     795,   -1.666667e+00,    5.000000e+00,   -3.333333e+00
+     796,   -1.666667e+00,    5.000000e+00,   -4.166667e+00
+     797,   -2.500000e+00,    5.000000e+00,    4.166667e+00
+     798,   -2.500000e+00,    5.000000e+00,    3.333333e+00
+     799,   -2.500000e+00,    5.000000e+00,    2.500000e+00
+     800,   -2.500000e+00,    5.000000e+00,    1.666667e+00
+     801,   -2.500000e+00,    5.000000e+00,    8.333333e-01
+     802,   -2.500000e+00,    5.000000e+00,    0.000000e+00
+     803,   -2.500000e+00,    5.000000e+00,   -8.333333e-01
+     804,   -2.500000e+00,    5.000000e+00,   -1.666667e+00
+     805,   -2.500000e+00,    5.000000e+00,   -2.500000e+00
+     806,   -2.500000e+00,    5.000000e+00,   -3.333333e+00
+     807,   -2.500000e+00,    5.000000e+00,   -4.166667e+00
+     808,   -3.333333e+00,    5.000000e+00,    4.166667e+00
+     809,   -3.333333e+00,    5.000000e+00,    3.333333e+00
+     810,   -3.333333e+00,    5.000000e+00,    2.500000e+00
+     811,   -3.333333e+00,    5.000000e+00,    1.666667e+00
+     812,   -3.333333e+00,    5.000000e+00,    8.333333e-01
+     813,   -3.333333e+00,    5.000000e+00,    0.000000e+00
+     814,   -3.333333e+00,    5.000000e+00,   -8.333333e-01
+     815,   -3.333333e+00,    5.000000e+00,   -1.666667e+00
+     816,   -3.333333e+00,    5.000000e+00,   -2.500000e+00
+     817,   -3.333333e+00,    5.000000e+00,   -3.333333e+00
+     818,   -3.333333e+00,    5.000000e+00,   -4.166667e+00
+     819,   -4.166667e+00,    5.000000e+00,    4.166667e+00
+     820,   -4.166667e+00,    5.000000e+00,    3.333333e+00
+     821,   -4.166667e+00,    5.000000e+00,    2.500000e+00
+     822,   -4.166667e+00,    5.000000e+00,    1.666667e+00
+     823,   -4.166667e+00,    5.000000e+00,    8.333333e-01
+     824,   -4.166667e+00,    5.000000e+00,    0.000000e+00
+     825,   -4.166667e+00,    5.000000e+00,   -8.333333e-01
+     826,   -4.166667e+00,    5.000000e+00,   -1.666667e+00
+     827,   -4.166667e+00,    5.000000e+00,   -2.500000e+00
+     828,   -4.166667e+00,    5.000000e+00,   -3.333333e+00
+     829,   -4.166667e+00,    5.000000e+00,   -4.166667e+00
+     830,    5.000000e+00,    4.166667e+00,   -5.000000e+00
+     831,    5.000000e+00,    3.333333e+00,   -5.000000e+00
+     832,    5.000000e+00,    2.500000e+00,   -5.000000e+00
+     833,    5.000000e+00,    1.666667e+00,   -5.000000e+00
+     834,    5.000000e+00,    8.333333e-01,   -5.000000e+00
+     835,    5.000000e+00,    0.000000e+00,   -5.000000e+00
+     836,    5.000000e+00,   -8.333333e-01,   -5.000000e+00
+     837,    5.000000e+00,   -1.666667e+00,   -5.000000e+00
+     838,    5.000000e+00,   -2.500000e+00,   -5.000000e+00
+     839,    5.000000e+00,   -3.333333e+00,   -5.000000e+00
+     840,    5.000000e+00,   -4.166667e+00,   -5.000000e+00
+     841,    5.000000e+00,   -4.166667e+00,    4.166667e+00
+     842,    5.000000e+00,   -4.166667e+00,    3.333333e+00
+     843,    5.000000e+00,   -4.166667e+00,    2.500000e+00
+     844,    5.000000e+00,   -4.166667e+00,    1.666667e+00
+     845,    5.000000e+00,   -4.166667e+00,    8.333333e-01
+     846,    5.000000e+00,   -4.166667e+00,    1.110223e-16
+     847,    5.000000e+00,   -4.166667e+00,   -8.333333e-01
+     848,    5.000000e+00,   -4.166667e+00,   -1.666667e+00
+     849,    5.000000e+00,   -4.166667e+00,   -2.500000e+00
+     850,    5.000000e+00,   -4.166667e+00,   -3.333333e+00
+     851,    5.000000e+00,   -4.166667e+00,   -4.166667e+00
+     852,    5.000000e+00,   -3.333333e+00,    4.166667e+00
+     853,    5.000000e+00,   -3.333333e+00,    3.333333e+00
+     854,    5.000000e+00,   -3.333333e+00,    2.500000e+00
+     855,    5.000000e+00,   -3.333333e+00,    1.666667e+00
+     856,    5.000000e+00,   -3.333333e+00,    8.333333e-01
+     857,    5.000000e+00,   -3.333333e+00,    5.551115e-17
+     858,    5.000000e+00,   -3.333333e+00,   -8.333333e-01
+     859,    5.000000e+00,   -3.333333e+00,   -1.666667e+00
+     860,    5.000000e+00,   -3.333333e+00,   -2.500000e+00
+     861,    5.000000e+00,   -3.333333e+00,   -3.333333e+00
+     862,    5.000000e+00,   -3.333333e+00,   -4.166667e+00
+     863,    5.000000e+00,   -2.500000e+00,    4.166667e+00
+     864,    5.000000e+00,   -2.500000e+00,    3.333333e+00
+     865,    5.000000e+00,   -2.500000e+00,    2.500000e+00
+     866,    5.000000e+00,   -2.500000e+00,    1.666667e+00
+     867,    5.000000e+00,   -2.500000e+00,    8.333333e-01
+     868,    5.000000e+00,   -2.500000e+00,    0.000000e+00
+     869,    5.000000e+00,   -2.500000e+00,   -8.333333e-01
+     870,    5.000000e+00,   -2.500000e+00,   -1.666667e+00
+     871,    5.000000e+00,   -2.500000e+00,   -2.500000e+00
+     872,    5.000000e+00,   -2.500000e+00,   -3.333333e+00
+     873,    5.000000e+00,   -2.500000e+00,   -4.166667e+00
+     874,    5.000000e+00,   -1.666667e+00,    4.166667e+00
+     875,    5.000000e+00,   -1.666667e+00,    3.333333e+00
+     876,    5.000000e+00,   -1.666667e+00,    2.500000e+00
+     877,    5.000000e+00,   -1.666667e+00,    1.666667e+00
+     878,    5.000000e+00,   -1.666667e+00,    8.333333e-01
+     879,    5.000000e+00,   -1.666667e+00,    0.000000e+00
+     880,    5.000000e+00,   -1.666667e+00,   -8.333333e-01
+     881,    5.000000e+00,   -1.666667e+00,   -1.666667e+00
+     882,    5.000000e+00,   -1.666667e+00,   -2.500000e+00
+     883,    5.000000e+00,   -1.666667e+00,   -3.333333e+00
+     884,    5.000000e+00,   -1.666667e+00,   -4.166667e+00
+     885,    5.000000e+00,   -8.333333e-01,    4.166667e+00
+     886,    5.000000e+00,   -8.333333e-01,    3.333333e+00
+     887,    5.000000e+00,   -8.333333e-01,    2.500000e+00
+     888,    5.000000e+00,   -8.333333e-01,    1.666667e+00
+     889,    5.000000e+00,   -8.333333e-01,    8.333333e-01
+     890,    5.000000e+00,   -8.333333e-01,    0.000000e+00
+     891,    5.000000e+00,   -8.333333e-01,   -8.333333e-01
+     892,    5.000000e+00,   -8.333333e-01,   -1.666667e+00
+     893,    5.000000e+00,   -8.333333e-01,   -2.500000e+00
+     894,    5.000000e+00,   -8.333333e-01,   -3.333333e+00
+     895,    5.000000e+00,   -8.333333e-01,   -4.166667e+00
+     896,    5.000000e+00,    0.000000e+00,    4.166667e+00
+     897,    5.000000e+00,    0.000000e+00,    3.333333e+00
+     898,    5.000000e+00,    0.000000e+00,    2.500000e+00
+     899,    5.000000e+00,    0.000000e+00,    1.666667e+00
+     900,    5.000000e+00,    0.000000e+00,    8.333333e-01
+     901,    5.000000e+00,    0.000000e+00,    0.000000e+00
+     902,    5.000000e+00,    0.000000e+00,   -8.333333e-01
+     903,    5.000000e+00,    0.000000e+00,   -1.666667e+00
+     904,    5.000000e+00,    0.000000e+00,   -2.500000e+00
+     905,    5.000000e+00,    0.000000e+00,   -3.333333e+00
+     906,    5.000000e+00,    0.000000e+00,   -4.166667e+00
+     907,    5.000000e+00,    8.333333e-01,    4.166667e+00
+     908,    5.000000e+00,    8.333333e-01,    3.333333e+00
+     909,    5.000000e+00,    8.333333e-01,    2.500000e+00
+     910,    5.000000e+00,    8.333333e-01,    1.666667e+00
+     911,    5.000000e+00,    8.333333e-01,    8.333333e-01
+     912,    5.000000e+00,    8.333333e-01,    0.000000e+00
+     913,    5.000000e+00,    8.333333e-01,   -8.333333e-01
+     914,    5.000000e+00,    8.333333e-01,   -1.666667e+00
+     915,    5.000000e+00,    8.333333e-01,   -2.500000e+00
+     916,    5.000000e+00,    8.333333e-01,   -3.333333e+00
+     917,    5.000000e+00,    8.333333e-01,   -4.166667e+00
+     918,    5.000000e+00,    1.666667e+00,    4.166667e+00
+     919,    5.000000e+00,    1.666667e+00,    3.333333e+00
+     920,    5.000000e+00,    1.666667e+00,    2.500000e+00
+     921,    5.000000e+00,    1.666667e+00,    1.666667e+00
+     922,    5.000000e+00,    1.666667e+00,    8.333333e-01
+     923,    5.000000e+00,    1.666667e+00,    0.000000e+00
+     924,    5.000000e+00,    1.666667e+00,   -8.333333e-01
+     925,    5.000000e+00,    1.666667e+00,   -1.666667e+00
+     926,    5.000000e+00,    1.666667e+00,   -2.500000e+00
+     927,    5.000000e+00,    1.666667e+00,   -3.333333e+00
+     928,    5.000000e+00,    1.666667e+00,   -4.166667e+00
+     929,    5.000000e+00,    2.500000e+00,    4.166667e+00
+     930,    5.000000e+00,    2.500000e+00,    3.333333e+00
+     931,    5.000000e+00,    2.500000e+00,    2.500000e+00
+     932,    5.000000e+00,    2.500000e+00,    1.666667e+00
+     933,    5.000000e+00,    2.500000e+00,    8.333333e-01
+     934,    5.000000e+00,    2.500000e+00,    0.000000e+00
+     935,    5.000000e+00,    2.500000e+00,   -8.333333e-01
+     936,    5.000000e+00,    2.500000e+00,   -1.666667e+00
+     937,    5.000000e+00,    2.500000e+00,   -2.500000e+00
+     938,    5.000000e+00,    2.500000e+00,   -3.333333e+00
+     939,    5.000000e+00,    2.500000e+00,   -4.166667e+00
+     940,    5.000000e+00,    3.333333e+00,    4.166667e+00
+     941,    5.000000e+00,    3.333333e+00,    3.333333e+00
+     942,    5.000000e+00,    3.333333e+00,    2.500000e+00
+     943,    5.000000e+00,    3.333333e+00,    1.666667e+00
+     944,    5.000000e+00,    3.333333e+00,    8.333333e-01
+     945,    5.000000e+00,    3.333333e+00,    0.000000e+00
+     946,    5.000000e+00,    3.333333e+00,   -8.333333e-01
+     947,    5.000000e+00,    3.333333e+00,   -1.666667e+00
+     948,    5.000000e+00,    3.333333e+00,   -2.500000e+00
+     949,    5.000000e+00,    3.333333e+00,   -3.333333e+00
+     950,    5.000000e+00,    3.333333e+00,   -4.166667e+00
+     951,    5.000000e+00,    4.166667e+00,    4.166667e+00
+     952,    5.000000e+00,    4.166667e+00,    3.333333e+00
+     953,    5.000000e+00,    4.166667e+00,    2.500000e+00
+     954,    5.000000e+00,    4.166667e+00,    1.666667e+00
+     955,    5.000000e+00,    4.166667e+00,    8.333333e-01
+     956,    5.000000e+00,    4.166667e+00,    0.000000e+00
+     957,    5.000000e+00,    4.166667e+00,   -8.333333e-01
+     958,    5.000000e+00,    4.166667e+00,   -1.666667e+00
+     959,    5.000000e+00,    4.166667e+00,   -2.500000e+00
+     960,    5.000000e+00,    4.166667e+00,   -3.333333e+00
+     961,    5.000000e+00,    4.166667e+00,   -4.166667e+00
+     962,    4.080156e+00,   -4.221993e+00,    7.471849e-17
+     963,    3.914282e+00,   -3.394158e+00,    7.690399e-17
+     964,    3.518431e+00,   -2.364823e+00,    7.927738e-17
+     965,    3.816228e+00,   -1.321296e+00,    8.295216e-17
+     966,    3.911426e+00,   -3.828604e-01,    8.594363e-17
+     967,    3.851744e+00,    6.169215e-01,    8.883830e-17
+     968,    4.367777e+00,    1.067366e+00,    9.112772e-17
+     969,    4.258286e+00,    1.745177e+00,    9.296492e-17
+     970,    4.164414e+00,    2.588980e+00,    9.532904e-17
+     971,    4.251528e+00,    3.377333e+00,    9.785505e-17
+     972,    4.212534e+00,    4.172338e+00,    1.001722e-16
+     973,   -4.232909e+00,    4.245850e+00,    8.506704e-17
+     974,   -4.232408e+00,    3.535725e+00,    8.293499e-17
+     975,   -4.001597e+00,    2.823007e+00,    8.121310e-17
+     976,   -3.578650e+00,    1.965393e+00,    7.940467e-17
+     977,   -3.858582e+00,    9.162757e-01,    7.574552e-17
+     978,   -3.924184e+00,    5.869503e-04,    7.287608e-17
+     979,   -3.854392e+00,   -9.140208e-01,    7.025558e-17
+     980,   -3.577153e+00,   -1.964790e+00,    6.760257e-17
+     981,   -4.001482e+00,   -2.822856e+00,    6.425522e-17
+     982,   -4.229138e+00,   -3.536852e+00,    6.169751e-17
+     983,   -4.228407e+00,   -4.248024e+00,    5.956274e-17
+     984,    3.470223e+00,    4.122143e+00,    9.867435e-17
+     985,    2.713818e+00,    3.926913e+00,    9.671530e-17
+     986,    1.853334e+00,    3.838186e+00,    9.488728e-17
+     987,    1.028238e+00,    3.903866e+00,    9.358725e-17
+     988,    1.782734e-01,    3.943301e+00,    9.216327e-17
+     989,   -7.250577e-01,    3.895979e+00,    9.038185e-17
+     990,   -1.805122e+00,    3.691458e+00,    8.780755e-17
+     991,   -2.772073e+00,    4.056834e+00,    8.715028e-17
+     992,   -3.511985e+00,    4.257900e+00,    8.641149e-17
+     993,   -3.509611e+00,   -4.260109e+00,    6.083085e-17
+     994,   -2.773226e+00,   -4.054954e+00,    6.278337e-17
+     995,   -1.801321e+00,   -3.692164e+00,    6.563678e-17
+     996,   -6.983753e-01,   -3.907437e+00,    6.699169e-17
+     997,    2.795061e-01,   -3.912741e+00,    6.875032e-17
+     998,    1.393456e+00,   -3.736053e+00,    7.130252e-17
+     999,    2.163497e+00,   -4.175723e+00,    7.137930e-17
+    1000,    2.639438e+00,   -4.508871e+00,    7.124234e-17
+    1001,    3.242527e+00,   -4.347026e+00,    7.282289e-17
+    1002,   -2.641377e+00,    2.809676e+00,    8.364145e-17
+    1003,   -3.667918e+00,    3.703972e+00,    8.446472e-17
+    1004,   -3.249197e+00,    3.318617e+00,    8.406711e-17
+    1005,    3.159382e+00,    2.755193e+00,    9.400445e-17
+    1006,    3.447440e+00,    1.687950e+00,    9.132158e-17
+    1007,    2.452949e+00,   -3.007055e+00,    7.541482e-17
+    1008,    3.618349e+00,    3.412924e+00,    9.681292e-17
+    1009,   -2.646526e+00,   -2.806103e+00,    6.676438e-17
+    1010,   -3.250904e+00,   -3.317987e+00,    6.413011e-17
+    1011,   -3.663878e+00,   -3.706025e+00,    6.221516e-17
+    1012,    2.937419e+00,   -3.705826e+00,    7.419514e-17
+    1013,    4.080156e+00,   -4.221993e+00,    8.333333e-01
+    1014,    3.914282e+00,   -3.394158e+00,    8.333333e-01
+    1015,    3.518431e+00,   -2.364823e+00,    8.333333e-01
+    1016,    3.816228e+00,   -1.321296e+00,    8.333333e-01
+    1017,    3.911426e+00,   -3.828604e-01,    8.333333e-01
+    1018,    3.851744e+00,    6.169215e-01,    8.333333e-01
+    1019,    4.367777e+00,    1.067366e+00,    8.333333e-01
+    1020,    4.258286e+00,    1.745177e+00,    8.333333e-01
+    1021,    4.164414e+00,    2.588980e+00,    8.333333e-01
+    1022,    4.251528e+00,    3.377333e+00,    8.333333e-01
+    1023,    4.212534e+00,    4.172338e+00,    8.333333e-01
+    1024,   -4.232909e+00,    4.245850e+00,    8.333333e-01
+    1025,   -4.232408e+00,    3.535725e+00,    8.333333e-01
+    1026,   -4.001597e+00,    2.823007e+00,    8.333333e-01
+    1027,   -3.578650e+00,    1.965393e+00,    8.333333e-01
+    1028,   -3.858582e+00,    9.162757e-01,    8.333333e-01
+    1029,   -3.924184e+00,    5.869503e-04,    8.333333e-01
+    1030,   -3.854392e+00,   -9.140208e-01,    8.333333e-01
+    1031,   -3.577153e+00,   -1.964790e+00,    8.333333e-01
+    1032,   -4.001482e+00,   -2.822856e+00,    8.333333e-01
+    1033,   -4.229138e+00,   -3.536852e+00,    8.333333e-01
+    1034,   -4.228407e+00,   -4.248024e+00,    8.333333e-01
+    1035,    3.470223e+00,    4.122143e+00,    8.333333e-01
+    1036,    2.713818e+00,    3.926913e+00,    8.333333e-01
+    1037,    1.853334e+00,    3.838186e+00,    8.333333e-01
+    1038,    1.028238e+00,    3.903866e+00,    8.333333e-01
+    1039,    1.782734e-01,    3.943301e+00,    8.333333e-01
+    1040,   -7.250577e-01,    3.895979e+00,    8.333333e-01
+    1041,   -1.805122e+00,    3.691458e+00,    8.333333e-01
+    1042,   -2.772073e+00,    4.056834e+00,    8.333333e-01
+    1043,   -3.511985e+00,    4.257900e+00,    8.333333e-01
+    1044,   -3.509611e+00,   -4.260109e+00,    8.333333e-01
+    1045,   -2.773226e+00,   -4.054954e+00,    8.333333e-01
+    1046,   -1.801321e+00,   -3.692164e+00,    8.333333e-01
+    1047,   -6.983753e-01,   -3.907437e+00,    8.333333e-01
+    1048,    2.795061e-01,   -3.912741e+00,    8.333333e-01
+    1049,    1.393456e+00,   -3.736053e+00,    8.333333e-01
+    1050,    2.163497e+00,   -4.175723e+00,    8.333333e-01
+    1051,    2.639438e+00,   -4.508871e+00,    8.333333e-01
+    1052,    3.242527e+00,   -4.347026e+00,    8.333333e-01
+    1053,   -2.641377e+00,    2.809676e+00,    8.333333e-01
+    1054,   -3.667918e+00,    3.703972e+00,    8.333333e-01
+    1055,   -3.249197e+00,    3.318617e+00,    8.333333e-01
+    1056,    3.159382e+00,    2.755193e+00,    8.333333e-01
+    1057,    3.447440e+00,    1.687950e+00,    8.333333e-01
+    1058,    2.452949e+00,   -3.007055e+00,    8.333333e-01
+    1059,    3.618349e+00,    3.412924e+00,    8.333333e-01
+    1060,   -2.646526e+00,   -2.806103e+00,    8.333333e-01
+    1061,   -3.250904e+00,   -3.317987e+00,    8.333333e-01
+    1062,   -3.663878e+00,   -3.706025e+00,    8.333333e-01
+    1063,    2.937419e+00,   -3.705826e+00,    8.333333e-01
+    1064,    4.080156e+00,   -4.221993e+00,    1.666667e+00
+    1065,    3.914282e+00,   -3.394158e+00,    1.666667e+00
+    1066,    3.518431e+00,   -2.364823e+00,    1.666667e+00
+    1067,    3.816228e+00,   -1.321296e+00,    1.666667e+00
+    1068,    3.911426e+00,   -3.828604e-01,    1.666667e+00
+    1069,    3.851744e+00,    6.169215e-01,    1.666667e+00
+    1070,    4.367777e+00,    1.067366e+00,    1.666667e+00
+    1071,    4.258286e+00,    1.745177e+00,    1.666667e+00
+    1072,    4.164414e+00,    2.588980e+00,    1.666667e+00
+    1073,    4.251528e+00,    3.377333e+00,    1.666667e+00
+    1074,    4.212534e+00,    4.172338e+00,    1.666667e+00
+    1075,   -4.232909e+00,    4.245850e+00,    1.666667e+00
+    1076,   -4.232408e+00,    3.535725e+00,    1.666667e+00
+    1077,   -4.001597e+00,    2.823007e+00,    1.666667e+00
+    1078,   -3.578650e+00,    1.965393e+00,    1.666667e+00
+    1079,   -3.858582e+00,    9.162757e-01,    1.666667e+00
+    1080,   -3.924184e+00,    5.869503e-04,    1.666667e+00
+    1081,   -3.854392e+00,   -9.140208e-01,    1.666667e+00
+    1082,   -3.577153e+00,   -1.964790e+00,    1.666667e+00
+    1083,   -4.001482e+00,   -2.822856e+00,    1.666667e+00
+    1084,   -4.229138e+00,   -3.536852e+00,    1.666667e+00
+    1085,   -4.228407e+00,   -4.248024e+00,    1.666667e+00
+    1086,    3.470223e+00,    4.122143e+00,    1.666667e+00
+    1087,    2.713818e+00,    3.926913e+00,    1.666667e+00
+    1088,    1.853334e+00,    3.838186e+00,    1.666667e+00
+    1089,    1.028238e+00,    3.903866e+00,    1.666667e+00
+    1090,    1.782734e-01,    3.943301e+00,    1.666667e+00
+    1091,   -7.250577e-01,    3.895979e+00,    1.666667e+00
+    1092,   -1.805122e+00,    3.691458e+00,    1.666667e+00
+    1093,   -2.772073e+00,    4.056834e+00,    1.666667e+00
+    1094,   -3.511985e+00,    4.257900e+00,    1.666667e+00
+    1095,   -3.509611e+00,   -4.260109e+00,    1.666667e+00
+    1096,   -2.773226e+00,   -4.054954e+00,    1.666667e+00
+    1097,   -1.801321e+00,   -3.692164e+00,    1.666667e+00
+    1098,   -6.983753e-01,   -3.907437e+00,    1.666667e+00
+    1099,    2.795061e-01,   -3.912741e+00,    1.666667e+00
+    1100,    1.393456e+00,   -3.736053e+00,    1.666667e+00
+    1101,    2.163497e+00,   -4.175723e+00,    1.666667e+00
+    1102,    2.639438e+00,   -4.508871e+00,    1.666667e+00
+    1103,    3.242527e+00,   -4.347026e+00,    1.666667e+00
+    1104,   -2.641377e+00,    2.809676e+00,    1.666667e+00
+    1105,   -3.667918e+00,    3.703972e+00,    1.666667e+00
+    1106,   -3.249197e+00,    3.318617e+00,    1.666667e+00
+    1107,    3.159382e+00,    2.755193e+00,    1.666667e+00
+    1108,    3.447440e+00,    1.687950e+00,    1.666667e+00
+    1109,    2.452949e+00,   -3.007055e+00,    1.666667e+00
+    1110,    3.618349e+00,    3.412924e+00,    1.666667e+00
+    1111,   -2.646526e+00,   -2.806103e+00,    1.666667e+00
+    1112,   -3.250904e+00,   -3.317987e+00,    1.666667e+00
+    1113,   -3.663878e+00,   -3.706025e+00,    1.666667e+00
+    1114,    2.937419e+00,   -3.705826e+00,    1.666667e+00
+    1115,    4.080156e+00,   -4.221993e+00,    2.500000e+00
+    1116,    3.914282e+00,   -3.394158e+00,    2.500000e+00
+    1117,    3.518431e+00,   -2.364823e+00,    2.500000e+00
+    1118,    3.816228e+00,   -1.321296e+00,    2.500000e+00
+    1119,    3.911426e+00,   -3.828604e-01,    2.500000e+00
+    1120,    3.851744e+00,    6.169215e-01,    2.500000e+00
+    1121,    4.367777e+00,    1.067366e+00,    2.500000e+00
+    1122,    4.258286e+00,    1.745177e+00,    2.500000e+00
+    1123,    4.164414e+00,    2.588980e+00,    2.500000e+00
+    1124,    4.251528e+00,    3.377333e+00,    2.500000e+00
+    1125,    4.212534e+00,    4.172338e+00,    2.500000e+00
+    1126,   -4.232909e+00,    4.245850e+00,    2.500000e+00
+    1127,   -4.232408e+00,    3.535725e+00,    2.500000e+00
+    1128,   -4.001597e+00,    2.823007e+00,    2.500000e+00
+    1129,   -3.578650e+00,    1.965393e+00,    2.500000e+00
+    1130,   -3.858582e+00,    9.162757e-01,    2.500000e+00
+    1131,   -3.924184e+00,    5.869503e-04,    2.500000e+00
+    1132,   -3.854392e+00,   -9.140208e-01,    2.500000e+00
+    1133,   -3.577153e+00,   -1.964790e+00,    2.500000e+00
+    1134,   -4.001482e+00,   -2.822856e+00,    2.500000e+00
+    1135,   -4.229138e+00,   -3.536852e+00,    2.500000e+00
+    1136,   -4.228407e+00,   -4.248024e+00,    2.500000e+00
+    1137,    3.470223e+00,    4.122143e+00,    2.500000e+00
+    1138,    2.713818e+00,    3.926913e+00,    2.500000e+00
+    1139,    1.853334e+00,    3.838186e+00,    2.500000e+00
+    1140,    1.028238e+00,    3.903866e+00,    2.500000e+00
+    1141,    1.782734e-01,    3.943301e+00,    2.500000e+00
+    1142,   -7.250577e-01,    3.895979e+00,    2.500000e+00
+    1143,   -1.805122e+00,    3.691458e+00,    2.500000e+00
+    1144,   -2.772073e+00,    4.056834e+00,    2.500000e+00
+    1145,   -3.511985e+00,    4.257900e+00,    2.500000e+00
+    1146,   -3.509611e+00,   -4.260109e+00,    2.500000e+00
+    1147,   -2.773226e+00,   -4.054954e+00,    2.500000e+00
+    1148,   -1.801321e+00,   -3.692164e+00,    2.500000e+00
+    1149,   -6.983753e-01,   -3.907437e+00,    2.500000e+00
+    1150,    2.795061e-01,   -3.912741e+00,    2.500000e+00
+    1151,    1.393456e+00,   -3.736053e+00,    2.500000e+00
+    1152,    2.163497e+00,   -4.175723e+00,    2.500000e+00
+    1153,    2.639438e+00,   -4.508871e+00,    2.500000e+00
+    1154,    3.242527e+00,   -4.347026e+00,    2.500000e+00
+    1155,   -2.641377e+00,    2.809676e+00,    2.500000e+00
+    1156,   -3.667918e+00,    3.703972e+00,    2.500000e+00
+    1157,   -3.249197e+00,    3.318617e+00,    2.500000e+00
+    1158,    3.159382e+00,    2.755193e+00,    2.500000e+00
+    1159,    3.447440e+00,    1.687950e+00,    2.500000e+00
+    1160,    2.452949e+00,   -3.007055e+00,    2.500000e+00
+    1161,    3.618349e+00,    3.412924e+00,    2.500000e+00
+    1162,   -2.646526e+00,   -2.806103e+00,    2.500000e+00
+    1163,   -3.250904e+00,   -3.317987e+00,    2.500000e+00
+    1164,   -3.663878e+00,   -3.706025e+00,    2.500000e+00
+    1165,    2.937419e+00,   -3.705826e+00,    2.500000e+00
+    1166,    4.080156e+00,   -4.221993e+00,    3.333333e+00
+    1167,    3.914282e+00,   -3.394158e+00,    3.333333e+00
+    1168,    3.518431e+00,   -2.364823e+00,    3.333333e+00
+    1169,    3.816228e+00,   -1.321296e+00,    3.333333e+00
+    1170,    3.911426e+00,   -3.828604e-01,    3.333333e+00
+    1171,    3.851744e+00,    6.169215e-01,    3.333333e+00
+    1172,    4.367777e+00,    1.067366e+00,    3.333333e+00
+    1173,    4.258286e+00,    1.745177e+00,    3.333333e+00
+    1174,    4.164414e+00,    2.588980e+00,    3.333333e+00
+    1175,    4.251528e+00,    3.377333e+00,    3.333333e+00
+    1176,    4.212534e+00,    4.172338e+00,    3.333333e+00
+    1177,   -4.232909e+00,    4.245850e+00,    3.333333e+00
+    1178,   -4.232408e+00,    3.535725e+00,    3.333333e+00
+    1179,   -4.001597e+00,    2.823007e+00,    3.333333e+00
+    1180,   -3.578650e+00,    1.965393e+00,    3.333333e+00
+    1181,   -3.858582e+00,    9.162757e-01,    3.333333e+00
+    1182,   -3.924184e+00,    5.869503e-04,    3.333333e+00
+    1183,   -3.854392e+00,   -9.140208e-01,    3.333333e+00
+    1184,   -3.577153e+00,   -1.964790e+00,    3.333333e+00
+    1185,   -4.001482e+00,   -2.822856e+00,    3.333333e+00
+    1186,   -4.229138e+00,   -3.536852e+00,    3.333333e+00
+    1187,   -4.228407e+00,   -4.248024e+00,    3.333333e+00
+    1188,    3.470223e+00,    4.122143e+00,    3.333333e+00
+    1189,    2.713818e+00,    3.926913e+00,    3.333333e+00
+    1190,    1.853334e+00,    3.838186e+00,    3.333333e+00
+    1191,    1.028238e+00,    3.903866e+00,    3.333333e+00
+    1192,    1.782734e-01,    3.943301e+00,    3.333333e+00
+    1193,   -7.250577e-01,    3.895979e+00,    3.333333e+00
+    1194,   -1.805122e+00,    3.691458e+00,    3.333333e+00
+    1195,   -2.772073e+00,    4.056834e+00,    3.333333e+00
+    1196,   -3.511985e+00,    4.257900e+00,    3.333333e+00
+    1197,   -3.509611e+00,   -4.260109e+00,    3.333333e+00
+    1198,   -2.773226e+00,   -4.054954e+00,    3.333333e+00
+    1199,   -1.801321e+00,   -3.692164e+00,    3.333333e+00
+    1200,   -6.983753e-01,   -3.907437e+00,    3.333333e+00
+    1201,    2.795061e-01,   -3.912741e+00,    3.333333e+00
+    1202,    1.393456e+00,   -3.736053e+00,    3.333333e+00
+    1203,    2.163497e+00,   -4.175723e+00,    3.333333e+00
+    1204,    2.639438e+00,   -4.508871e+00,    3.333333e+00
+    1205,    3.242527e+00,   -4.347026e+00,    3.333333e+00
+    1206,   -2.641377e+00,    2.809676e+00,    3.333333e+00
+    1207,   -3.667918e+00,    3.703972e+00,    3.333333e+00
+    1208,   -3.249197e+00,    3.318617e+00,    3.333333e+00
+    1209,    3.159382e+00,    2.755193e+00,    3.333333e+00
+    1210,    3.447440e+00,    1.687950e+00,    3.333333e+00
+    1211,    2.452949e+00,   -3.007055e+00,    3.333333e+00
+    1212,    3.618349e+00,    3.412924e+00,    3.333333e+00
+    1213,   -2.646526e+00,   -2.806103e+00,    3.333333e+00
+    1214,   -3.250904e+00,   -3.317987e+00,    3.333333e+00
+    1215,   -3.663878e+00,   -3.706025e+00,    3.333333e+00
+    1216,    2.937419e+00,   -3.705826e+00,    3.333333e+00
+    1217,    4.080156e+00,   -4.221993e+00,    4.166667e+00
+    1218,    3.914282e+00,   -3.394158e+00,    4.166667e+00
+    1219,    3.518431e+00,   -2.364823e+00,    4.166667e+00
+    1220,    3.816228e+00,   -1.321296e+00,    4.166667e+00
+    1221,    3.911426e+00,   -3.828604e-01,    4.166667e+00
+    1222,    3.851744e+00,    6.169215e-01,    4.166667e+00
+    1223,    4.367777e+00,    1.067366e+00,    4.166667e+00
+    1224,    4.258286e+00,    1.745177e+00,    4.166667e+00
+    1225,    4.164414e+00,    2.588980e+00,    4.166667e+00
+    1226,    4.251528e+00,    3.377333e+00,    4.166667e+00
+    1227,    4.212534e+00,    4.172338e+00,    4.166667e+00
+    1228,   -4.232909e+00,    4.245850e+00,    4.166667e+00
+    1229,   -4.232408e+00,    3.535725e+00,    4.166667e+00
+    1230,   -4.001597e+00,    2.823007e+00,    4.166667e+00
+    1231,   -3.578650e+00,    1.965393e+00,    4.166667e+00
+    1232,   -3.858582e+00,    9.162757e-01,    4.166667e+00
+    1233,   -3.924184e+00,    5.869503e-04,    4.166667e+00
+    1234,   -3.854392e+00,   -9.140208e-01,    4.166667e+00
+    1235,   -3.577153e+00,   -1.964790e+00,    4.166667e+00
+    1236,   -4.001482e+00,   -2.822856e+00,    4.166667e+00
+    1237,   -4.229138e+00,   -3.536852e+00,    4.166667e+00
+    1238,   -4.228407e+00,   -4.248024e+00,    4.166667e+00
+    1239,    3.470223e+00,    4.122143e+00,    4.166667e+00
+    1240,    2.713818e+00,    3.926913e+00,    4.166667e+00
+    1241,    1.853334e+00,    3.838186e+00,    4.166667e+00
+    1242,    1.028238e+00,    3.903866e+00,    4.166667e+00
+    1243,    1.782734e-01,    3.943301e+00,    4.166667e+00
+    1244,   -7.250577e-01,    3.895979e+00,    4.166667e+00
+    1245,   -1.805122e+00,    3.691458e+00,    4.166667e+00
+    1246,   -2.772073e+00,    4.056834e+00,    4.166667e+00
+    1247,   -3.511985e+00,    4.257900e+00,    4.166667e+00
+    1248,   -3.509611e+00,   -4.260109e+00,    4.166667e+00
+    1249,   -2.773226e+00,   -4.054954e+00,    4.166667e+00
+    1250,   -1.801321e+00,   -3.692164e+00,    4.166667e+00
+    1251,   -6.983753e-01,   -3.907437e+00,    4.166667e+00
+    1252,    2.795061e-01,   -3.912741e+00,    4.166667e+00
+    1253,    1.393456e+00,   -3.736053e+00,    4.166667e+00
+    1254,    2.163497e+00,   -4.175723e+00,    4.166667e+00
+    1255,    2.639438e+00,   -4.508871e+00,    4.166667e+00
+    1256,    3.242527e+00,   -4.347026e+00,    4.166667e+00
+    1257,   -2.641377e+00,    2.809676e+00,    4.166667e+00
+    1258,   -3.667918e+00,    3.703972e+00,    4.166667e+00
+    1259,   -3.249197e+00,    3.318617e+00,    4.166667e+00
+    1260,    3.159382e+00,    2.755193e+00,    4.166667e+00
+    1261,    3.447440e+00,    1.687950e+00,    4.166667e+00
+    1262,    2.452949e+00,   -3.007055e+00,    4.166667e+00
+    1263,    3.618349e+00,    3.412924e+00,    4.166667e+00
+    1264,   -2.646526e+00,   -2.806103e+00,    4.166667e+00
+    1265,   -3.250904e+00,   -3.317987e+00,    4.166667e+00
+    1266,   -3.663878e+00,   -3.706025e+00,    4.166667e+00
+    1267,    2.937419e+00,   -3.705826e+00,    4.166667e+00
+    1268,    4.080156e+00,   -4.221993e+00,   -8.333333e-01
+    1269,    3.914282e+00,   -3.394158e+00,   -8.333333e-01
+    1270,    3.518431e+00,   -2.364823e+00,   -8.333333e-01
+    1271,    3.816228e+00,   -1.321296e+00,   -8.333333e-01
+    1272,    3.911426e+00,   -3.828604e-01,   -8.333333e-01
+    1273,    3.851744e+00,    6.169215e-01,   -8.333333e-01
+    1274,    4.367777e+00,    1.067366e+00,   -8.333333e-01
+    1275,    4.258286e+00,    1.745177e+00,   -8.333333e-01
+    1276,    4.164414e+00,    2.588980e+00,   -8.333333e-01
+    1277,    4.251528e+00,    3.377333e+00,   -8.333333e-01
+    1278,    4.212534e+00,    4.172338e+00,   -8.333333e-01
+    1279,   -4.232909e+00,    4.245850e+00,   -8.333333e-01
+    1280,   -4.232408e+00,    3.535725e+00,   -8.333333e-01
+    1281,   -4.001597e+00,    2.823007e+00,   -8.333333e-01
+    1282,   -3.578650e+00,    1.965393e+00,   -8.333333e-01
+    1283,   -3.858582e+00,    9.162757e-01,   -8.333333e-01
+    1284,   -3.924184e+00,    5.869503e-04,   -8.333333e-01
+    1285,   -3.854392e+00,   -9.140208e-01,   -8.333333e-01
+    1286,   -3.577153e+00,   -1.964790e+00,   -8.333333e-01
+    1287,   -4.001482e+00,   -2.822856e+00,   -8.333333e-01
+    1288,   -4.229138e+00,   -3.536852e+00,   -8.333333e-01
+    1289,   -4.228407e+00,   -4.248024e+00,   -8.333333e-01
+    1290,    3.470223e+00,    4.122143e+00,   -8.333333e-01
+    1291,    2.713818e+00,    3.926913e+00,   -8.333333e-01
+    1292,    1.853334e+00,    3.838186e+00,   -8.333333e-01
+    1293,    1.028238e+00,    3.903866e+00,   -8.333333e-01
+    1294,    1.782734e-01,    3.943301e+00,   -8.333333e-01
+    1295,   -7.250577e-01,    3.895979e+00,   -8.333333e-01
+    1296,   -1.805122e+00,    3.691458e+00,   -8.333333e-01
+    1297,   -2.772073e+00,    4.056834e+00,   -8.333333e-01
+    1298,   -3.511985e+00,    4.257900e+00,   -8.333333e-01
+    1299,   -3.509611e+00,   -4.260109e+00,   -8.333333e-01
+    1300,   -2.773226e+00,   -4.054954e+00,   -8.333333e-01
+    1301,   -1.801321e+00,   -3.692164e+00,   -8.333333e-01
+    1302,   -6.983753e-01,   -3.907437e+00,   -8.333333e-01
+    1303,    2.795061e-01,   -3.912741e+00,   -8.333333e-01
+    1304,    1.393456e+00,   -3.736053e+00,   -8.333333e-01
+    1305,    2.163497e+00,   -4.175723e+00,   -8.333333e-01
+    1306,    2.639438e+00,   -4.508871e+00,   -8.333333e-01
+    1307,    3.242527e+00,   -4.347026e+00,   -8.333333e-01
+    1308,   -2.641377e+00,    2.809676e+00,   -8.333333e-01
+    1309,   -3.667918e+00,    3.703972e+00,   -8.333333e-01
+    1310,   -3.249197e+00,    3.318617e+00,   -8.333333e-01
+    1311,    3.159382e+00,    2.755193e+00,   -8.333333e-01
+    1312,    3.447440e+00,    1.687950e+00,   -8.333333e-01
+    1313,    2.452949e+00,   -3.007055e+00,   -8.333333e-01
+    1314,    3.618349e+00,    3.412924e+00,   -8.333333e-01
+    1315,   -2.646526e+00,   -2.806103e+00,   -8.333333e-01
+    1316,   -3.250904e+00,   -3.317987e+00,   -8.333333e-01
+    1317,   -3.663878e+00,   -3.706025e+00,   -8.333333e-01
+    1318,    2.937419e+00,   -3.705826e+00,   -8.333333e-01
+    1319,    4.080156e+00,   -4.221993e+00,   -1.666667e+00
+    1320,    3.914282e+00,   -3.394158e+00,   -1.666667e+00
+    1321,    3.518431e+00,   -2.364823e+00,   -1.666667e+00
+    1322,    3.816228e+00,   -1.321296e+00,   -1.666667e+00
+    1323,    3.911426e+00,   -3.828604e-01,   -1.666667e+00
+    1324,    3.851744e+00,    6.169215e-01,   -1.666667e+00
+    1325,    4.367777e+00,    1.067366e+00,   -1.666667e+00
+    1326,    4.258286e+00,    1.745177e+00,   -1.666667e+00
+    1327,    4.164414e+00,    2.588980e+00,   -1.666667e+00
+    1328,    4.251528e+00,    3.377333e+00,   -1.666667e+00
+    1329,    4.212534e+00,    4.172338e+00,   -1.666667e+00
+    1330,   -4.232909e+00,    4.245850e+00,   -1.666667e+00
+    1331,   -4.232408e+00,    3.535725e+00,   -1.666667e+00
+    1332,   -4.001597e+00,    2.823007e+00,   -1.666667e+00
+    1333,   -3.578650e+00,    1.965393e+00,   -1.666667e+00
+    1334,   -3.858582e+00,    9.162757e-01,   -1.666667e+00
+    1335,   -3.924184e+00,    5.869503e-04,   -1.666667e+00
+    1336,   -3.854392e+00,   -9.140208e-01,   -1.666667e+00
+    1337,   -3.577153e+00,   -1.964790e+00,   -1.666667e+00
+    1338,   -4.001482e+00,   -2.822856e+00,   -1.666667e+00
+    1339,   -4.229138e+00,   -3.536852e+00,   -1.666667e+00
+    1340,   -4.228407e+00,   -4.248024e+00,   -1.666667e+00
+    1341,    3.470223e+00,    4.122143e+00,   -1.666667e+00
+    1342,    2.713818e+00,    3.926913e+00,   -1.666667e+00
+    1343,    1.853334e+00,    3.838186e+00,   -1.666667e+00
+    1344,    1.028238e+00,    3.903866e+00,   -1.666667e+00
+    1345,    1.782734e-01,    3.943301e+00,   -1.666667e+00
+    1346,   -7.250577e-01,    3.895979e+00,   -1.666667e+00
+    1347,   -1.805122e+00,    3.691458e+00,   -1.666667e+00
+    1348,   -2.772073e+00,    4.056834e+00,   -1.666667e+00
+    1349,   -3.511985e+00,    4.257900e+00,   -1.666667e+00
+    1350,   -3.509611e+00,   -4.260109e+00,   -1.666667e+00
+    1351,   -2.773226e+00,   -4.054954e+00,   -1.666667e+00
+    1352,   -1.801321e+00,   -3.692164e+00,   -1.666667e+00
+    1353,   -6.983753e-01,   -3.907437e+00,   -1.666667e+00
+    1354,    2.795061e-01,   -3.912741e+00,   -1.666667e+00
+    1355,    1.393456e+00,   -3.736053e+00,   -1.666667e+00
+    1356,    2.163497e+00,   -4.175723e+00,   -1.666667e+00
+    1357,    2.639438e+00,   -4.508871e+00,   -1.666667e+00
+    1358,    3.242527e+00,   -4.347026e+00,   -1.666667e+00
+    1359,   -2.641377e+00,    2.809676e+00,   -1.666667e+00
+    1360,   -3.667918e+00,    3.703972e+00,   -1.666667e+00
+    1361,   -3.249197e+00,    3.318617e+00,   -1.666667e+00
+    1362,    3.159382e+00,    2.755193e+00,   -1.666667e+00
+    1363,    3.447440e+00,    1.687950e+00,   -1.666667e+00
+    1364,    2.452949e+00,   -3.007055e+00,   -1.666667e+00
+    1365,    3.618349e+00,    3.412924e+00,   -1.666667e+00
+    1366,   -2.646526e+00,   -2.806103e+00,   -1.666667e+00
+    1367,   -3.250904e+00,   -3.317987e+00,   -1.666667e+00
+    1368,   -3.663878e+00,   -3.706025e+00,   -1.666667e+00
+    1369,    2.937419e+00,   -3.705826e+00,   -1.666667e+00
+    1370,    4.080156e+00,   -4.221993e+00,   -2.500000e+00
+    1371,    3.914282e+00,   -3.394158e+00,   -2.500000e+00
+    1372,    3.518431e+00,   -2.364823e+00,   -2.500000e+00
+    1373,    3.816228e+00,   -1.321296e+00,   -2.500000e+00
+    1374,    3.911426e+00,   -3.828604e-01,   -2.500000e+00
+    1375,    3.851744e+00,    6.169215e-01,   -2.500000e+00
+    1376,    4.367777e+00,    1.067366e+00,   -2.500000e+00
+    1377,    4.258286e+00,    1.745177e+00,   -2.500000e+00
+    1378,    4.164414e+00,    2.588980e+00,   -2.500000e+00
+    1379,    4.251528e+00,    3.377333e+00,   -2.500000e+00
+    1380,    4.212534e+00,    4.172338e+00,   -2.500000e+00
+    1381,   -4.232909e+00,    4.245850e+00,   -2.500000e+00
+    1382,   -4.232408e+00,    3.535725e+00,   -2.500000e+00
+    1383,   -4.001597e+00,    2.823007e+00,   -2.500000e+00
+    1384,   -3.578650e+00,    1.965393e+00,   -2.500000e+00
+    1385,   -3.858582e+00,    9.162757e-01,   -2.500000e+00
+    1386,   -3.924184e+00,    5.869503e-04,   -2.500000e+00
+    1387,   -3.854392e+00,   -9.140208e-01,   -2.500000e+00
+    1388,   -3.577153e+00,   -1.964790e+00,   -2.500000e+00
+    1389,   -4.001482e+00,   -2.822856e+00,   -2.500000e+00
+    1390,   -4.229138e+00,   -3.536852e+00,   -2.500000e+00
+    1391,   -4.228407e+00,   -4.248024e+00,   -2.500000e+00
+    1392,    3.470223e+00,    4.122143e+00,   -2.500000e+00
+    1393,    2.713818e+00,    3.926913e+00,   -2.500000e+00
+    1394,    1.853334e+00,    3.838186e+00,   -2.500000e+00
+    1395,    1.028238e+00,    3.903866e+00,   -2.500000e+00
+    1396,    1.782734e-01,    3.943301e+00,   -2.500000e+00
+    1397,   -7.250577e-01,    3.895979e+00,   -2.500000e+00
+    1398,   -1.805122e+00,    3.691458e+00,   -2.500000e+00
+    1399,   -2.772073e+00,    4.056834e+00,   -2.500000e+00
+    1400,   -3.511985e+00,    4.257900e+00,   -2.500000e+00
+    1401,   -3.509611e+00,   -4.260109e+00,   -2.500000e+00
+    1402,   -2.773226e+00,   -4.054954e+00,   -2.500000e+00
+    1403,   -1.801321e+00,   -3.692164e+00,   -2.500000e+00
+    1404,   -6.983753e-01,   -3.907437e+00,   -2.500000e+00
+    1405,    2.795061e-01,   -3.912741e+00,   -2.500000e+00
+    1406,    1.393456e+00,   -3.736053e+00,   -2.500000e+00
+    1407,    2.163497e+00,   -4.175723e+00,   -2.500000e+00
+    1408,    2.639438e+00,   -4.508871e+00,   -2.500000e+00
+    1409,    3.242527e+00,   -4.347026e+00,   -2.500000e+00
+    1410,   -2.641377e+00,    2.809676e+00,   -2.500000e+00
+    1411,   -3.667918e+00,    3.703972e+00,   -2.500000e+00
+    1412,   -3.249197e+00,    3.318617e+00,   -2.500000e+00
+    1413,    3.159382e+00,    2.755193e+00,   -2.500000e+00
+    1414,    3.447440e+00,    1.687950e+00,   -2.500000e+00
+    1415,    2.452949e+00,   -3.007055e+00,   -2.500000e+00
+    1416,    3.618349e+00,    3.412924e+00,   -2.500000e+00
+    1417,   -2.646526e+00,   -2.806103e+00,   -2.500000e+00
+    1418,   -3.250904e+00,   -3.317987e+00,   -2.500000e+00
+    1419,   -3.663878e+00,   -3.706025e+00,   -2.500000e+00
+    1420,    2.937419e+00,   -3.705826e+00,   -2.500000e+00
+    1421,    4.080156e+00,   -4.221993e+00,   -3.333333e+00
+    1422,    3.914282e+00,   -3.394158e+00,   -3.333333e+00
+    1423,    3.518431e+00,   -2.364823e+00,   -3.333333e+00
+    1424,    3.816228e+00,   -1.321296e+00,   -3.333333e+00
+    1425,    3.911426e+00,   -3.828604e-01,   -3.333333e+00
+    1426,    3.851744e+00,    6.169215e-01,   -3.333333e+00
+    1427,    4.367777e+00,    1.067366e+00,   -3.333333e+00
+    1428,    4.258286e+00,    1.745177e+00,   -3.333333e+00
+    1429,    4.164414e+00,    2.588980e+00,   -3.333333e+00
+    1430,    4.251528e+00,    3.377333e+00,   -3.333333e+00
+    1431,    4.212534e+00,    4.172338e+00,   -3.333333e+00
+    1432,   -4.232909e+00,    4.245850e+00,   -3.333333e+00
+    1433,   -4.232408e+00,    3.535725e+00,   -3.333333e+00
+    1434,   -4.001597e+00,    2.823007e+00,   -3.333333e+00
+    1435,   -3.578650e+00,    1.965393e+00,   -3.333333e+00
+    1436,   -3.858582e+00,    9.162757e-01,   -3.333333e+00
+    1437,   -3.924184e+00,    5.869503e-04,   -3.333333e+00
+    1438,   -3.854392e+00,   -9.140208e-01,   -3.333333e+00
+    1439,   -3.577153e+00,   -1.964790e+00,   -3.333333e+00
+    1440,   -4.001482e+00,   -2.822856e+00,   -3.333333e+00
+    1441,   -4.229138e+00,   -3.536852e+00,   -3.333333e+00
+    1442,   -4.228407e+00,   -4.248024e+00,   -3.333333e+00
+    1443,    3.470223e+00,    4.122143e+00,   -3.333333e+00
+    1444,    2.713818e+00,    3.926913e+00,   -3.333333e+00
+    1445,    1.853334e+00,    3.838186e+00,   -3.333333e+00
+    1446,    1.028238e+00,    3.903866e+00,   -3.333333e+00
+    1447,    1.782734e-01,    3.943301e+00,   -3.333333e+00
+    1448,   -7.250577e-01,    3.895979e+00,   -3.333333e+00
+    1449,   -1.805122e+00,    3.691458e+00,   -3.333333e+00
+    1450,   -2.772073e+00,    4.056834e+00,   -3.333333e+00
+    1451,   -3.511985e+00,    4.257900e+00,   -3.333333e+00
+    1452,   -3.509611e+00,   -4.260109e+00,   -3.333333e+00
+    1453,   -2.773226e+00,   -4.054954e+00,   -3.333333e+00
+    1454,   -1.801321e+00,   -3.692164e+00,   -3.333333e+00
+    1455,   -6.983753e-01,   -3.907437e+00,   -3.333333e+00
+    1456,    2.795061e-01,   -3.912741e+00,   -3.333333e+00
+    1457,    1.393456e+00,   -3.736053e+00,   -3.333333e+00
+    1458,    2.163497e+00,   -4.175723e+00,   -3.333333e+00
+    1459,    2.639438e+00,   -4.508871e+00,   -3.333333e+00
+    1460,    3.242527e+00,   -4.347026e+00,   -3.333333e+00
+    1461,   -2.641377e+00,    2.809676e+00,   -3.333333e+00
+    1462,   -3.667918e+00,    3.703972e+00,   -3.333333e+00
+    1463,   -3.249197e+00,    3.318617e+00,   -3.333333e+00
+    1464,    3.159382e+00,    2.755193e+00,   -3.333333e+00
+    1465,    3.447440e+00,    1.687950e+00,   -3.333333e+00
+    1466,    2.452949e+00,   -3.007055e+00,   -3.333333e+00
+    1467,    3.618349e+00,    3.412924e+00,   -3.333333e+00
+    1468,   -2.646526e+00,   -2.806103e+00,   -3.333333e+00
+    1469,   -3.250904e+00,   -3.317987e+00,   -3.333333e+00
+    1470,   -3.663878e+00,   -3.706025e+00,   -3.333333e+00
+    1471,    2.937419e+00,   -3.705826e+00,   -3.333333e+00
+    1472,    4.080156e+00,   -4.221993e+00,   -4.166667e+00
+    1473,    3.914282e+00,   -3.394158e+00,   -4.166667e+00
+    1474,    3.518431e+00,   -2.364823e+00,   -4.166667e+00
+    1475,    3.816228e+00,   -1.321296e+00,   -4.166667e+00
+    1476,    3.911426e+00,   -3.828604e-01,   -4.166667e+00
+    1477,    3.851744e+00,    6.169215e-01,   -4.166667e+00
+    1478,    4.367777e+00,    1.067366e+00,   -4.166667e+00
+    1479,    4.258286e+00,    1.745177e+00,   -4.166667e+00
+    1480,    4.164414e+00,    2.588980e+00,   -4.166667e+00
+    1481,    4.251528e+00,    3.377333e+00,   -4.166667e+00
+    1482,    4.212534e+00,    4.172338e+00,   -4.166667e+00
+    1483,   -4.232909e+00,    4.245850e+00,   -4.166667e+00
+    1484,   -4.232408e+00,    3.535725e+00,   -4.166667e+00
+    1485,   -4.001597e+00,    2.823007e+00,   -4.166667e+00
+    1486,   -3.578650e+00,    1.965393e+00,   -4.166667e+00
+    1487,   -3.858582e+00,    9.162757e-01,   -4.166667e+00
+    1488,   -3.924184e+00,    5.869503e-04,   -4.166667e+00
+    1489,   -3.854392e+00,   -9.140208e-01,   -4.166667e+00
+    1490,   -3.577153e+00,   -1.964790e+00,   -4.166667e+00
+    1491,   -4.001482e+00,   -2.822856e+00,   -4.166667e+00
+    1492,   -4.229138e+00,   -3.536852e+00,   -4.166667e+00
+    1493,   -4.228407e+00,   -4.248024e+00,   -4.166667e+00
+    1494,    3.470223e+00,    4.122143e+00,   -4.166667e+00
+    1495,    2.713818e+00,    3.926913e+00,   -4.166667e+00
+    1496,    1.853334e+00,    3.838186e+00,   -4.166667e+00
+    1497,    1.028238e+00,    3.903866e+00,   -4.166667e+00
+    1498,    1.782734e-01,    3.943301e+00,   -4.166667e+00
+    1499,   -7.250577e-01,    3.895979e+00,   -4.166667e+00
+    1500,   -1.805122e+00,    3.691458e+00,   -4.166667e+00
+    1501,   -2.772073e+00,    4.056834e+00,   -4.166667e+00
+    1502,   -3.511985e+00,    4.257900e+00,   -4.166667e+00
+    1503,   -3.509611e+00,   -4.260109e+00,   -4.166667e+00
+    1504,   -2.773226e+00,   -4.054954e+00,   -4.166667e+00
+    1505,   -1.801321e+00,   -3.692164e+00,   -4.166667e+00
+    1506,   -6.983753e-01,   -3.907437e+00,   -4.166667e+00
+    1507,    2.795061e-01,   -3.912741e+00,   -4.166667e+00
+    1508,    1.393456e+00,   -3.736053e+00,   -4.166667e+00
+    1509,    2.163497e+00,   -4.175723e+00,   -4.166667e+00
+    1510,    2.639438e+00,   -4.508871e+00,   -4.166667e+00
+    1511,    3.242527e+00,   -4.347026e+00,   -4.166667e+00
+    1512,   -2.641377e+00,    2.809676e+00,   -4.166667e+00
+    1513,   -3.667918e+00,    3.703972e+00,   -4.166667e+00
+    1514,   -3.249197e+00,    3.318617e+00,   -4.166667e+00
+    1515,    3.159382e+00,    2.755193e+00,   -4.166667e+00
+    1516,    3.447440e+00,    1.687950e+00,   -4.166667e+00
+    1517,    2.452949e+00,   -3.007055e+00,   -4.166667e+00
+    1518,    3.618349e+00,    3.412924e+00,   -4.166667e+00
+    1519,   -2.646526e+00,   -2.806103e+00,   -4.166667e+00
+    1520,   -3.250904e+00,   -3.317987e+00,   -4.166667e+00
+    1521,   -3.663878e+00,   -3.706025e+00,   -4.166667e+00
+    1522,    2.937419e+00,   -3.705826e+00,   -4.166667e+00
+    1523,    4.080156e+00,   -4.221993e+00,   -5.000000e+00
+    1524,    3.914282e+00,   -3.394158e+00,   -5.000000e+00
+    1525,    3.518431e+00,   -2.364823e+00,   -5.000000e+00
+    1526,    3.816228e+00,   -1.321296e+00,   -5.000000e+00
+    1527,    3.911426e+00,   -3.828604e-01,   -5.000000e+00
+    1528,    3.851744e+00,    6.169215e-01,   -5.000000e+00
+    1529,    4.367777e+00,    1.067366e+00,   -5.000000e+00
+    1530,    4.258286e+00,    1.745177e+00,   -5.000000e+00
+    1531,    4.164414e+00,    2.588980e+00,   -5.000000e+00
+    1532,    4.251528e+00,    3.377333e+00,   -5.000000e+00
+    1533,    4.212534e+00,    4.172338e+00,   -5.000000e+00
+    1534,   -4.232909e+00,    4.245850e+00,   -5.000000e+00
+    1535,   -4.232408e+00,    3.535725e+00,   -5.000000e+00
+    1536,   -4.001597e+00,    2.823007e+00,   -5.000000e+00
+    1537,   -3.578650e+00,    1.965393e+00,   -5.000000e+00
+    1538,   -3.858582e+00,    9.162757e-01,   -5.000000e+00
+    1539,   -3.924184e+00,    5.869503e-04,   -5.000000e+00
+    1540,   -3.854392e+00,   -9.140208e-01,   -5.000000e+00
+    1541,   -3.577153e+00,   -1.964790e+00,   -5.000000e+00
+    1542,   -4.001482e+00,   -2.822856e+00,   -5.000000e+00
+    1543,   -4.229138e+00,   -3.536852e+00,   -5.000000e+00
+    1544,   -4.228407e+00,   -4.248024e+00,   -5.000000e+00
+    1545,    3.470223e+00,    4.122143e+00,   -5.000000e+00
+    1546,    2.713818e+00,    3.926913e+00,   -5.000000e+00
+    1547,    1.853334e+00,    3.838186e+00,   -5.000000e+00
+    1548,    1.028238e+00,    3.903866e+00,   -5.000000e+00
+    1549,    1.782734e-01,    3.943301e+00,   -5.000000e+00
+    1550,   -7.250577e-01,    3.895979e+00,   -5.000000e+00
+    1551,   -1.805122e+00,    3.691458e+00,   -5.000000e+00
+    1552,   -2.772073e+00,    4.056834e+00,   -5.000000e+00
+    1553,   -3.511985e+00,    4.257900e+00,   -5.000000e+00
+    1554,   -3.509611e+00,   -4.260109e+00,   -5.000000e+00
+    1555,   -2.773226e+00,   -4.054954e+00,   -5.000000e+00
+    1556,   -1.801321e+00,   -3.692164e+00,   -5.000000e+00
+    1557,   -6.983753e-01,   -3.907437e+00,   -5.000000e+00
+    1558,    2.795061e-01,   -3.912741e+00,   -5.000000e+00
+    1559,    1.393456e+00,   -3.736053e+00,   -5.000000e+00
+    1560,    2.163497e+00,   -4.175723e+00,   -5.000000e+00
+    1561,    2.639438e+00,   -4.508871e+00,   -5.000000e+00
+    1562,    3.242527e+00,   -4.347026e+00,   -5.000000e+00
+    1563,   -2.641377e+00,    2.809676e+00,   -5.000000e+00
+    1564,   -3.667918e+00,    3.703972e+00,   -5.000000e+00
+    1565,   -3.249197e+00,    3.318617e+00,   -5.000000e+00
+    1566,    3.159382e+00,    2.755193e+00,   -5.000000e+00
+    1567,    3.447440e+00,    1.687950e+00,   -5.000000e+00
+    1568,    2.452949e+00,   -3.007055e+00,   -5.000000e+00
+    1569,    3.618349e+00,    3.412924e+00,   -5.000000e+00
+    1570,   -2.646526e+00,   -2.806103e+00,   -5.000000e+00
+    1571,   -3.250904e+00,   -3.317987e+00,   -5.000000e+00
+    1572,   -3.663878e+00,   -3.706025e+00,   -5.000000e+00
+    1573,    2.937419e+00,   -3.705826e+00,   -5.000000e+00
+**
+********************************** E L E M E N T S ****************************
+*ELEMENT, TYPE=C3D8R, ELSET=EB1
+       1,     410,     841,    1217,     531,      23,      25,      71,      70
+       2,     841,     852,    1218,    1217,      25,      26,      72,      71
+       3,     852,     863,    1219,    1218,      26,      27,      73,      72
+       4,     863,     874,    1220,    1219,      27,      28,      74,      73
+       5,     874,     885,    1221,    1220,      28,      29,      75,      74
+       6,     885,     896,    1222,    1221,      29,      30,      76,      75
+       7,     896,     907,    1223,    1222,      30,      31,      77,      76
+       8,     907,     918,    1224,    1223,      31,      32,     113,      77
+       9,     918,     929,    1225,    1224,      32,      33,      78,     113
+      10,     929,     940,    1226,    1225,      33,      34,      79,      78
+      11,     940,     951,    1227,    1226,      34,      35,      80,      79
+      12,     951,     687,     709,    1227,      35,      24,      37,      80
+      13,     543,     565,    1228,     819,      36,      49,      81,      47
+      14,     565,     576,    1229,    1228,      49,      50,      82,      81
+      15,     576,     587,    1230,    1229,      50,      51,      83,      82
+      16,     587,     598,    1231,    1230,      51,      52,      84,      83
+      17,     598,     609,    1232,    1231,      52,      53,     105,      84
+      18,     609,     620,    1233,    1232,      53,      54,     104,     105
+      19,     620,     631,    1234,    1233,      54,      55,     118,     104
+      20,     631,     642,    1235,    1234,      55,      56,     117,     118
+      21,     642,     653,    1236,    1235,      56,      57,      85,     117
+      22,     653,     664,    1237,    1236,      57,      58,      86,      85
+      23,     664,     675,    1238,    1237,      58,      59,      87,      86
+      24,     675,     387,     421,    1238,      59,      48,      60,      87
+      25,     709,     720,    1239,    1227,      37,      38,      88,      80
+      26,     720,     731,    1240,    1239,      38,      39,      89,      88
+      27,     731,     742,    1241,    1240,      39,      40,      90,      89
+      28,     742,     753,    1242,    1241,      40,      41,      91,      90
+      29,     753,     764,    1243,    1242,      41,      42,      92,      91
+      30,     764,     775,    1244,    1243,      42,      43,     108,      92
+      31,     775,     786,    1245,    1244,      43,      44,     107,     108
+      32,     786,     797,    1246,    1245,      44,      45,      93,     107
+      33,     797,     808,    1247,    1246,      45,      46,      94,      93
+      34,     808,     819,    1228,    1247,      46,      47,      81,      94
+      35,     421,     432,    1248,    1238,      60,      61,      95,      87
+      36,     432,     443,    1249,    1248,      61,      62,      96,      95
+      37,     443,     454,    1250,    1249,      62,      63,      97,      96
+      38,     454,     465,    1251,    1250,      63,      64,      98,      97
+      39,     465,     476,    1252,    1251,      64,      65,      99,      98
+      40,     476,     487,    1253,    1252,      65,      66,     100,      99
+      41,     487,     498,    1254,    1253,      66,      67,     101,     100
+      42,     498,     509,    1255,    1254,      67,      68,     102,     101
+      43,     509,     520,    1256,    1255,      68,      69,     103,     102
+      44,     520,     531,    1217,    1256,      69,      70,      71,     103
+      45,     154,     385,    1232,    1233,       1,       2,     105,     104
+      46,     385,     374,    1231,    1232,       2,       3,      84,     105
+      47,     374,     363,    1257,    1231,       3,       4,     106,      84
+      48,     363,     352,    1245,    1257,       4,       5,     107,     106
+      49,     352,     341,    1244,    1245,       5,       6,     108,     107
+      50,     341,     330,    1243,    1244,       6,       7,      92,     108
+      51,    1228,    1229,    1258,    1247,      81,      82,     109,      94
+      52,    1229,    1230,    1259,    1258,      82,      83,     110,     109
+      53,    1230,    1231,    1257,    1259,      83,      84,     106,     110
+      54,     330,     319,    1242,    1243,       7,       8,      91,      92
+      55,    1259,    1257,    1245,    1246,     110,     106,     107,      93
+      56,    1246,    1247,    1258,    1259,      93,      94,     109,     110
+      57,     319,     308,    1241,    1242,       8,       9,      90,      91
+      58,    1241,     308,    1260,    1240,      90,       9,     111,      89
+      59,     308,     297,    1261,    1260,       9,      10,     112,     111
+      60,     297,     286,    1222,    1261,      10,      11,      76,     112
+      61,     286,     275,    1221,    1222,      11,      12,      75,      76
+      62,     275,     264,    1220,    1221,      12,      13,      74,      75
+      63,     264,     253,    1219,    1220,      13,      14,      73,      74
+      64,    1222,    1223,    1224,    1261,      76,      77,     113,     112
+      65,     253,     242,    1262,    1219,      14,      15,     114,      73
+      66,     242,     231,    1253,    1262,      15,      16,     100,     114
+      67,     231,     220,    1252,    1253,      16,      17,      99,     100
+      68,     220,     209,    1251,    1252,      17,      18,      98,      99
+      69,    1261,    1224,    1225,    1260,     112,     113,      78,     111
+      70,     209,     198,    1250,    1251,      18,      19,      97,      98
+      71,    1225,    1226,    1263,    1260,      78,      79,     115,     111
+      72,    1227,    1239,    1263,    1226,      80,      88,     115,      79
+      73,    1240,    1260,    1263,    1239,      89,     111,     115,      88
+      74,     198,     187,    1264,    1250,      19,      20,     116,      97
+      75,     187,     176,    1235,    1264,      20,      21,     117,     116
+      76,     176,     165,    1234,    1235,      21,      22,     118,     117
+      77,    1234,     165,     154,    1233,     118,      22,       1,     104
+      78,    1235,    1236,    1265,    1264,     117,      85,     119,     116
+      79,    1236,    1237,    1266,    1265,      85,      86,     120,     119
+      80,    1237,    1238,    1248,    1266,      86,      87,      95,     120
+      81,    1249,    1250,    1264,    1265,      96,      97,     116,     119
+      82,    1265,    1266,    1248,    1249,     119,     120,      95,      96
+      83,    1217,    1218,    1267,    1256,      71,      72,     121,     103
+      84,    1219,    1262,    1267,    1218,      73,     114,     121,      72
+      85,    1253,    1254,    1267,    1262,     100,     101,     121,     114
+      86,    1254,    1255,    1256,    1267,     101,     102,     103,     121
+      87,     411,     842,    1166,     532,     410,     841,    1217,     531
+      88,     842,     853,    1167,    1166,     841,     852,    1218,    1217
+      89,     853,     864,    1168,    1167,     852,     863,    1219,    1218
+      90,     864,     875,    1169,    1168,     863,     874,    1220,    1219
+      91,     875,     886,    1170,    1169,     874,     885,    1221,    1220
+      92,     886,     897,    1171,    1170,     885,     896,    1222,    1221
+      93,     897,     908,    1172,    1171,     896,     907,    1223,    1222
+      94,     908,     919,    1173,    1172,     907,     918,    1224,    1223
+      95,     919,     930,    1174,    1173,     918,     929,    1225,    1224
+      96,     930,     941,    1175,    1174,     929,     940,    1226,    1225
+      97,     941,     952,    1176,    1175,     940,     951,    1227,    1226
+      98,     952,     688,     710,    1176,     951,     687,     709,    1227
+      99,     544,     566,    1177,     820,     543,     565,    1228,     819
+     100,     566,     577,    1178,    1177,     565,     576,    1229,    1228
+     101,     577,     588,    1179,    1178,     576,     587,    1230,    1229
+     102,     588,     599,    1180,    1179,     587,     598,    1231,    1230
+     103,     599,     610,    1181,    1180,     598,     609,    1232,    1231
+     104,     610,     621,    1182,    1181,     609,     620,    1233,    1232
+     105,     621,     632,    1183,    1182,     620,     631,    1234,    1233
+     106,     632,     643,    1184,    1183,     631,     642,    1235,    1234
+     107,     643,     654,    1185,    1184,     642,     653,    1236,    1235
+     108,     654,     665,    1186,    1185,     653,     664,    1237,    1236
+     109,     665,     676,    1187,    1186,     664,     675,    1238,    1237
+     110,     676,     388,     422,    1187,     675,     387,     421,    1238
+     111,     710,     721,    1188,    1176,     709,     720,    1239,    1227
+     112,     721,     732,    1189,    1188,     720,     731,    1240,    1239
+     113,     732,     743,    1190,    1189,     731,     742,    1241,    1240
+     114,     743,     754,    1191,    1190,     742,     753,    1242,    1241
+     115,     754,     765,    1192,    1191,     753,     764,    1243,    1242
+     116,     765,     776,    1193,    1192,     764,     775,    1244,    1243
+     117,     776,     787,    1194,    1193,     775,     786,    1245,    1244
+     118,     787,     798,    1195,    1194,     786,     797,    1246,    1245
+     119,     798,     809,    1196,    1195,     797,     808,    1247,    1246
+     120,     809,     820,    1177,    1196,     808,     819,    1228,    1247
+     121,     422,     433,    1197,    1187,     421,     432,    1248,    1238
+     122,     433,     444,    1198,    1197,     432,     443,    1249,    1248
+     123,     444,     455,    1199,    1198,     443,     454,    1250,    1249
+     124,     455,     466,    1200,    1199,     454,     465,    1251,    1250
+     125,     466,     477,    1201,    1200,     465,     476,    1252,    1251
+     126,     477,     488,    1202,    1201,     476,     487,    1253,    1252
+     127,     488,     499,    1203,    1202,     487,     498,    1254,    1253
+     128,     499,     510,    1204,    1203,     498,     509,    1255,    1254
+     129,     510,     521,    1205,    1204,     509,     520,    1256,    1255
+     130,     521,     532,    1166,    1205,     520,     531,    1217,    1256
+     131,     153,     384,    1181,    1182,     154,     385,    1232,    1233
+     132,     384,     373,    1180,    1181,     385,     374,    1231,    1232
+     133,     373,     362,    1206,    1180,     374,     363,    1257,    1231
+     134,     362,     351,    1194,    1206,     363,     352,    1245,    1257
+     135,     351,     340,    1193,    1194,     352,     341,    1244,    1245
+     136,     340,     329,    1192,    1193,     341,     330,    1243,    1244
+     137,    1177,    1178,    1207,    1196,    1228,    1229,    1258,    1247
+     138,    1178,    1179,    1208,    1207,    1229,    1230,    1259,    1258
+     139,    1179,    1180,    1206,    1208,    1230,    1231,    1257,    1259
+     140,     329,     318,    1191,    1192,     330,     319,    1242,    1243
+     141,    1208,    1206,    1194,    1195,    1259,    1257,    1245,    1246
+     142,    1195,    1196,    1207,    1208,    1246,    1247,    1258,    1259
+     143,     318,     307,    1190,    1191,     319,     308,    1241,    1242
+     144,    1190,     307,    1209,    1189,    1241,     308,    1260,    1240
+     145,     307,     296,    1210,    1209,     308,     297,    1261,    1260
+     146,     296,     285,    1171,    1210,     297,     286,    1222,    1261
+     147,     285,     274,    1170,    1171,     286,     275,    1221,    1222
+     148,     274,     263,    1169,    1170,     275,     264,    1220,    1221
+     149,     263,     252,    1168,    1169,     264,     253,    1219,    1220
+     150,    1171,    1172,    1173,    1210,    1222,    1223,    1224,    1261
+     151,     252,     241,    1211,    1168,     253,     242,    1262,    1219
+     152,     241,     230,    1202,    1211,     242,     231,    1253,    1262
+     153,     230,     219,    1201,    1202,     231,     220,    1252,    1253
+     154,     219,     208,    1200,    1201,     220,     209,    1251,    1252
+     155,    1210,    1173,    1174,    1209,    1261,    1224,    1225,    1260
+     156,     208,     197,    1199,    1200,     209,     198,    1250,    1251
+     157,    1174,    1175,    1212,    1209,    1225,    1226,    1263,    1260
+     158,    1176,    1188,    1212,    1175,    1227,    1239,    1263,    1226
+     159,    1189,    1209,    1212,    1188,    1240,    1260,    1263,    1239
+     160,     197,     186,    1213,    1199,     198,     187,    1264,    1250
+     161,     186,     175,    1184,    1213,     187,     176,    1235,    1264
+     162,     175,     164,    1183,    1184,     176,     165,    1234,    1235
+     163,    1183,     164,     153,    1182,    1234,     165,     154,    1233
+     164,    1184,    1185,    1214,    1213,    1235,    1236,    1265,    1264
+     165,    1185,    1186,    1215,    1214,    1236,    1237,    1266,    1265
+     166,    1186,    1187,    1197,    1215,    1237,    1238,    1248,    1266
+     167,    1198,    1199,    1213,    1214,    1249,    1250,    1264,    1265
+     168,    1214,    1215,    1197,    1198,    1265,    1266,    1248,    1249
+     169,    1166,    1167,    1216,    1205,    1217,    1218,    1267,    1256
+     170,    1168,    1211,    1216,    1167,    1219,    1262,    1267,    1218
+     171,    1202,    1203,    1216,    1211,    1253,    1254,    1267,    1262
+     172,    1203,    1204,    1205,    1216,    1254,    1255,    1256,    1267
+     173,     412,     843,    1115,     533,     411,     842,    1166,     532
+     174,     843,     854,    1116,    1115,     842,     853,    1167,    1166
+     175,     854,     865,    1117,    1116,     853,     864,    1168,    1167
+     176,     865,     876,    1118,    1117,     864,     875,    1169,    1168
+     177,     876,     887,    1119,    1118,     875,     886,    1170,    1169
+     178,     887,     898,    1120,    1119,     886,     897,    1171,    1170
+     179,     898,     909,    1121,    1120,     897,     908,    1172,    1171
+     180,     909,     920,    1122,    1121,     908,     919,    1173,    1172
+     181,     920,     931,    1123,    1122,     919,     930,    1174,    1173
+     182,     931,     942,    1124,    1123,     930,     941,    1175,    1174
+     183,     942,     953,    1125,    1124,     941,     952,    1176,    1175
+     184,     953,     689,     711,    1125,     952,     688,     710,    1176
+     185,     545,     567,    1126,     821,     544,     566,    1177,     820
+     186,     567,     578,    1127,    1126,     566,     577,    1178,    1177
+     187,     578,     589,    1128,    1127,     577,     588,    1179,    1178
+     188,     589,     600,    1129,    1128,     588,     599,    1180,    1179
+     189,     600,     611,    1130,    1129,     599,     610,    1181,    1180
+     190,     611,     622,    1131,    1130,     610,     621,    1182,    1181
+     191,     622,     633,    1132,    1131,     621,     632,    1183,    1182
+     192,     633,     644,    1133,    1132,     632,     643,    1184,    1183
+     193,     644,     655,    1134,    1133,     643,     654,    1185,    1184
+     194,     655,     666,    1135,    1134,     654,     665,    1186,    1185
+     195,     666,     677,    1136,    1135,     665,     676,    1187,    1186
+     196,     677,     389,     423,    1136,     676,     388,     422,    1187
+     197,     711,     722,    1137,    1125,     710,     721,    1188,    1176
+     198,     722,     733,    1138,    1137,     721,     732,    1189,    1188
+     199,     733,     744,    1139,    1138,     732,     743,    1190,    1189
+     200,     744,     755,    1140,    1139,     743,     754,    1191,    1190
+     201,     755,     766,    1141,    1140,     754,     765,    1192,    1191
+     202,     766,     777,    1142,    1141,     765,     776,    1193,    1192
+     203,     777,     788,    1143,    1142,     776,     787,    1194,    1193
+     204,     788,     799,    1144,    1143,     787,     798,    1195,    1194
+     205,     799,     810,    1145,    1144,     798,     809,    1196,    1195
+     206,     810,     821,    1126,    1145,     809,     820,    1177,    1196
+     207,     423,     434,    1146,    1136,     422,     433,    1197,    1187
+     208,     434,     445,    1147,    1146,     433,     444,    1198,    1197
+     209,     445,     456,    1148,    1147,     444,     455,    1199,    1198
+     210,     456,     467,    1149,    1148,     455,     466,    1200,    1199
+     211,     467,     478,    1150,    1149,     466,     477,    1201,    1200
+     212,     478,     489,    1151,    1150,     477,     488,    1202,    1201
+     213,     489,     500,    1152,    1151,     488,     499,    1203,    1202
+     214,     500,     511,    1153,    1152,     499,     510,    1204,    1203
+     215,     511,     522,    1154,    1153,     510,     521,    1205,    1204
+     216,     522,     533,    1115,    1154,     521,     532,    1166,    1205
+     217,     152,     383,    1130,    1131,     153,     384,    1181,    1182
+     218,     383,     372,    1129,    1130,     384,     373,    1180,    1181
+     219,     372,     361,    1155,    1129,     373,     362,    1206,    1180
+     220,     361,     350,    1143,    1155,     362,     351,    1194,    1206
+     221,     350,     339,    1142,    1143,     351,     340,    1193,    1194
+     222,     339,     328,    1141,    1142,     340,     329,    1192,    1193
+     223,    1126,    1127,    1156,    1145,    1177,    1178,    1207,    1196
+     224,    1127,    1128,    1157,    1156,    1178,    1179,    1208,    1207
+     225,    1128,    1129,    1155,    1157,    1179,    1180,    1206,    1208
+     226,     328,     317,    1140,    1141,     329,     318,    1191,    1192
+     227,    1157,    1155,    1143,    1144,    1208,    1206,    1194,    1195
+     228,    1144,    1145,    1156,    1157,    1195,    1196,    1207,    1208
+     229,     317,     306,    1139,    1140,     318,     307,    1190,    1191
+     230,    1139,     306,    1158,    1138,    1190,     307,    1209,    1189
+     231,     306,     295,    1159,    1158,     307,     296,    1210,    1209
+     232,     295,     284,    1120,    1159,     296,     285,    1171,    1210
+     233,     284,     273,    1119,    1120,     285,     274,    1170,    1171
+     234,     273,     262,    1118,    1119,     274,     263,    1169,    1170
+     235,     262,     251,    1117,    1118,     263,     252,    1168,    1169
+     236,    1120,    1121,    1122,    1159,    1171,    1172,    1173,    1210
+     237,     251,     240,    1160,    1117,     252,     241,    1211,    1168
+     238,     240,     229,    1151,    1160,     241,     230,    1202,    1211
+     239,     229,     218,    1150,    1151,     230,     219,    1201,    1202
+     240,     218,     207,    1149,    1150,     219,     208,    1200,    1201
+     241,    1159,    1122,    1123,    1158,    1210,    1173,    1174,    1209
+     242,     207,     196,    1148,    1149,     208,     197,    1199,    1200
+     243,    1123,    1124,    1161,    1158,    1174,    1175,    1212,    1209
+     244,    1125,    1137,    1161,    1124,    1176,    1188,    1212,    1175
+     245,    1138,    1158,    1161,    1137,    1189,    1209,    1212,    1188
+     246,     196,     185,    1162,    1148,     197,     186,    1213,    1199
+     247,     185,     174,    1133,    1162,     186,     175,    1184,    1213
+     248,     174,     163,    1132,    1133,     175,     164,    1183,    1184
+     249,    1132,     163,     152,    1131,    1183,     164,     153,    1182
+     250,    1133,    1134,    1163,    1162,    1184,    1185,    1214,    1213
+     251,    1134,    1135,    1164,    1163,    1185,    1186,    1215,    1214
+     252,    1135,    1136,    1146,    1164,    1186,    1187,    1197,    1215
+     253,    1147,    1148,    1162,    1163,    1198,    1199,    1213,    1214
+     254,    1163,    1164,    1146,    1147,    1214,    1215,    1197,    1198
+     255,    1115,    1116,    1165,    1154,    1166,    1167,    1216,    1205
+     256,    1117,    1160,    1165,    1116,    1168,    1211,    1216,    1167
+     257,    1151,    1152,    1165,    1160,    1202,    1203,    1216,    1211
+     258,    1152,    1153,    1154,    1165,    1203,    1204,    1205,    1216
+     259,     413,     844,    1064,     534,     412,     843,    1115,     533
+     260,     844,     855,    1065,    1064,     843,     854,    1116,    1115
+     261,     855,     866,    1066,    1065,     854,     865,    1117,    1116
+     262,     866,     877,    1067,    1066,     865,     876,    1118,    1117
+     263,     877,     888,    1068,    1067,     876,     887,    1119,    1118
+     264,     888,     899,    1069,    1068,     887,     898,    1120,    1119
+     265,     899,     910,    1070,    1069,     898,     909,    1121,    1120
+     266,     910,     921,    1071,    1070,     909,     920,    1122,    1121
+     267,     921,     932,    1072,    1071,     920,     931,    1123,    1122
+     268,     932,     943,    1073,    1072,     931,     942,    1124,    1123
+     269,     943,     954,    1074,    1073,     942,     953,    1125,    1124
+     270,     954,     690,     712,    1074,     953,     689,     711,    1125
+     271,     546,     568,    1075,     822,     545,     567,    1126,     821
+     272,     568,     579,    1076,    1075,     567,     578,    1127,    1126
+     273,     579,     590,    1077,    1076,     578,     589,    1128,    1127
+     274,     590,     601,    1078,    1077,     589,     600,    1129,    1128
+     275,     601,     612,    1079,    1078,     600,     611,    1130,    1129
+     276,     612,     623,    1080,    1079,     611,     622,    1131,    1130
+     277,     623,     634,    1081,    1080,     622,     633,    1132,    1131
+     278,     634,     645,    1082,    1081,     633,     644,    1133,    1132
+     279,     645,     656,    1083,    1082,     644,     655,    1134,    1133
+     280,     656,     667,    1084,    1083,     655,     666,    1135,    1134
+     281,     667,     678,    1085,    1084,     666,     677,    1136,    1135
+     282,     678,     390,     424,    1085,     677,     389,     423,    1136
+     283,     712,     723,    1086,    1074,     711,     722,    1137,    1125
+     284,     723,     734,    1087,    1086,     722,     733,    1138,    1137
+     285,     734,     745,    1088,    1087,     733,     744,    1139,    1138
+     286,     745,     756,    1089,    1088,     744,     755,    1140,    1139
+     287,     756,     767,    1090,    1089,     755,     766,    1141,    1140
+     288,     767,     778,    1091,    1090,     766,     777,    1142,    1141
+     289,     778,     789,    1092,    1091,     777,     788,    1143,    1142
+     290,     789,     800,    1093,    1092,     788,     799,    1144,    1143
+     291,     800,     811,    1094,    1093,     799,     810,    1145,    1144
+     292,     811,     822,    1075,    1094,     810,     821,    1126,    1145
+     293,     424,     435,    1095,    1085,     423,     434,    1146,    1136
+     294,     435,     446,    1096,    1095,     434,     445,    1147,    1146
+     295,     446,     457,    1097,    1096,     445,     456,    1148,    1147
+     296,     457,     468,    1098,    1097,     456,     467,    1149,    1148
+     297,     468,     479,    1099,    1098,     467,     478,    1150,    1149
+     298,     479,     490,    1100,    1099,     478,     489,    1151,    1150
+     299,     490,     501,    1101,    1100,     489,     500,    1152,    1151
+     300,     501,     512,    1102,    1101,     500,     511,    1153,    1152
+     301,     512,     523,    1103,    1102,     511,     522,    1154,    1153
+     302,     523,     534,    1064,    1103,     522,     533,    1115,    1154
+     303,     151,     382,    1079,    1080,     152,     383,    1130,    1131
+     304,     382,     371,    1078,    1079,     383,     372,    1129,    1130
+     305,     371,     360,    1104,    1078,     372,     361,    1155,    1129
+     306,     360,     349,    1092,    1104,     361,     350,    1143,    1155
+     307,     349,     338,    1091,    1092,     350,     339,    1142,    1143
+     308,     338,     327,    1090,    1091,     339,     328,    1141,    1142
+     309,    1075,    1076,    1105,    1094,    1126,    1127,    1156,    1145
+     310,    1076,    1077,    1106,    1105,    1127,    1128,    1157,    1156
+     311,    1077,    1078,    1104,    1106,    1128,    1129,    1155,    1157
+     312,     327,     316,    1089,    1090,     328,     317,    1140,    1141
+     313,    1106,    1104,    1092,    1093,    1157,    1155,    1143,    1144
+     314,    1093,    1094,    1105,    1106,    1144,    1145,    1156,    1157
+     315,     316,     305,    1088,    1089,     317,     306,    1139,    1140
+     316,    1088,     305,    1107,    1087,    1139,     306,    1158,    1138
+     317,     305,     294,    1108,    1107,     306,     295,    1159,    1158
+     318,     294,     283,    1069,    1108,     295,     284,    1120,    1159
+     319,     283,     272,    1068,    1069,     284,     273,    1119,    1120
+     320,     272,     261,    1067,    1068,     273,     262,    1118,    1119
+     321,     261,     250,    1066,    1067,     262,     251,    1117,    1118
+     322,    1069,    1070,    1071,    1108,    1120,    1121,    1122,    1159
+     323,     250,     239,    1109,    1066,     251,     240,    1160,    1117
+     324,     239,     228,    1100,    1109,     240,     229,    1151,    1160
+     325,     228,     217,    1099,    1100,     229,     218,    1150,    1151
+     326,     217,     206,    1098,    1099,     218,     207,    1149,    1150
+     327,    1108,    1071,    1072,    1107,    1159,    1122,    1123,    1158
+     328,     206,     195,    1097,    1098,     207,     196,    1148,    1149
+     329,    1072,    1073,    1110,    1107,    1123,    1124,    1161,    1158
+     330,    1074,    1086,    1110,    1073,    1125,    1137,    1161,    1124
+     331,    1087,    1107,    1110,    1086,    1138,    1158,    1161,    1137
+     332,     195,     184,    1111,    1097,     196,     185,    1162,    1148
+     333,     184,     173,    1082,    1111,     185,     174,    1133,    1162
+     334,     173,     162,    1081,    1082,     174,     163,    1132,    1133
+     335,    1081,     162,     151,    1080,    1132,     163,     152,    1131
+     336,    1082,    1083,    1112,    1111,    1133,    1134,    1163,    1162
+     337,    1083,    1084,    1113,    1112,    1134,    1135,    1164,    1163
+     338,    1084,    1085,    1095,    1113,    1135,    1136,    1146,    1164
+     339,    1096,    1097,    1111,    1112,    1147,    1148,    1162,    1163
+     340,    1112,    1113,    1095,    1096,    1163,    1164,    1146,    1147
+     341,    1064,    1065,    1114,    1103,    1115,    1116,    1165,    1154
+     342,    1066,    1109,    1114,    1065,    1117,    1160,    1165,    1116
+     343,    1100,    1101,    1114,    1109,    1151,    1152,    1165,    1160
+     344,    1101,    1102,    1103,    1114,    1152,    1153,    1154,    1165
+     345,     414,     845,    1013,     535,     413,     844,    1064,     534
+     346,     845,     856,    1014,    1013,     844,     855,    1065,    1064
+     347,     856,     867,    1015,    1014,     855,     866,    1066,    1065
+     348,     867,     878,    1016,    1015,     866,     877,    1067,    1066
+     349,     878,     889,    1017,    1016,     877,     888,    1068,    1067
+     350,     889,     900,    1018,    1017,     888,     899,    1069,    1068
+     351,     900,     911,    1019,    1018,     899,     910,    1070,    1069
+     352,     911,     922,    1020,    1019,     910,     921,    1071,    1070
+     353,     922,     933,    1021,    1020,     921,     932,    1072,    1071
+     354,     933,     944,    1022,    1021,     932,     943,    1073,    1072
+     355,     944,     955,    1023,    1022,     943,     954,    1074,    1073
+     356,     955,     691,     713,    1023,     954,     690,     712,    1074
+     357,     547,     569,    1024,     823,     546,     568,    1075,     822
+     358,     569,     580,    1025,    1024,     568,     579,    1076,    1075
+     359,     580,     591,    1026,    1025,     579,     590,    1077,    1076
+     360,     591,     602,    1027,    1026,     590,     601,    1078,    1077
+     361,     602,     613,    1028,    1027,     601,     612,    1079,    1078
+     362,     613,     624,    1029,    1028,     612,     623,    1080,    1079
+     363,     624,     635,    1030,    1029,     623,     634,    1081,    1080
+     364,     635,     646,    1031,    1030,     634,     645,    1082,    1081
+     365,     646,     657,    1032,    1031,     645,     656,    1083,    1082
+     366,     657,     668,    1033,    1032,     656,     667,    1084,    1083
+     367,     668,     679,    1034,    1033,     667,     678,    1085,    1084
+     368,     679,     391,     425,    1034,     678,     390,     424,    1085
+     369,     713,     724,    1035,    1023,     712,     723,    1086,    1074
+     370,     724,     735,    1036,    1035,     723,     734,    1087,    1086
+     371,     735,     746,    1037,    1036,     734,     745,    1088,    1087
+     372,     746,     757,    1038,    1037,     745,     756,    1089,    1088
+     373,     757,     768,    1039,    1038,     756,     767,    1090,    1089
+     374,     768,     779,    1040,    1039,     767,     778,    1091,    1090
+     375,     779,     790,    1041,    1040,     778,     789,    1092,    1091
+     376,     790,     801,    1042,    1041,     789,     800,    1093,    1092
+     377,     801,     812,    1043,    1042,     800,     811,    1094,    1093
+     378,     812,     823,    1024,    1043,     811,     822,    1075,    1094
+     379,     425,     436,    1044,    1034,     424,     435,    1095,    1085
+     380,     436,     447,    1045,    1044,     435,     446,    1096,    1095
+     381,     447,     458,    1046,    1045,     446,     457,    1097,    1096
+     382,     458,     469,    1047,    1046,     457,     468,    1098,    1097
+     383,     469,     480,    1048,    1047,     468,     479,    1099,    1098
+     384,     480,     491,    1049,    1048,     479,     490,    1100,    1099
+     385,     491,     502,    1050,    1049,     490,     501,    1101,    1100
+     386,     502,     513,    1051,    1050,     501,     512,    1102,    1101
+     387,     513,     524,    1052,    1051,     512,     523,    1103,    1102
+     388,     524,     535,    1013,    1052,     523,     534,    1064,    1103
+     389,     150,     381,    1028,    1029,     151,     382,    1079,    1080
+     390,     381,     370,    1027,    1028,     382,     371,    1078,    1079
+     391,     370,     359,    1053,    1027,     371,     360,    1104,    1078
+     392,     359,     348,    1041,    1053,     360,     349,    1092,    1104
+     393,     348,     337,    1040,    1041,     349,     338,    1091,    1092
+     394,     337,     326,    1039,    1040,     338,     327,    1090,    1091
+     395,    1024,    1025,    1054,    1043,    1075,    1076,    1105,    1094
+     396,    1025,    1026,    1055,    1054,    1076,    1077,    1106,    1105
+     397,    1026,    1027,    1053,    1055,    1077,    1078,    1104,    1106
+     398,     326,     315,    1038,    1039,     327,     316,    1089,    1090
+     399,    1055,    1053,    1041,    1042,    1106,    1104,    1092,    1093
+     400,    1042,    1043,    1054,    1055,    1093,    1094,    1105,    1106
+     401,     315,     304,    1037,    1038,     316,     305,    1088,    1089
+     402,    1037,     304,    1056,    1036,    1088,     305,    1107,    1087
+     403,     304,     293,    1057,    1056,     305,     294,    1108,    1107
+     404,     293,     282,    1018,    1057,     294,     283,    1069,    1108
+     405,     282,     271,    1017,    1018,     283,     272,    1068,    1069
+     406,     271,     260,    1016,    1017,     272,     261,    1067,    1068
+     407,     260,     249,    1015,    1016,     261,     250,    1066,    1067
+     408,    1018,    1019,    1020,    1057,    1069,    1070,    1071,    1108
+     409,     249,     238,    1058,    1015,     250,     239,    1109,    1066
+     410,     238,     227,    1049,    1058,     239,     228,    1100,    1109
+     411,     227,     216,    1048,    1049,     228,     217,    1099,    1100
+     412,     216,     205,    1047,    1048,     217,     206,    1098,    1099
+     413,    1057,    1020,    1021,    1056,    1108,    1071,    1072,    1107
+     414,     205,     194,    1046,    1047,     206,     195,    1097,    1098
+     415,    1021,    1022,    1059,    1056,    1072,    1073,    1110,    1107
+     416,    1023,    1035,    1059,    1022,    1074,    1086,    1110,    1073
+     417,    1036,    1056,    1059,    1035,    1087,    1107,    1110,    1086
+     418,     194,     183,    1060,    1046,     195,     184,    1111,    1097
+     419,     183,     172,    1031,    1060,     184,     173,    1082,    1111
+     420,     172,     161,    1030,    1031,     173,     162,    1081,    1082
+     421,    1030,     161,     150,    1029,    1081,     162,     151,    1080
+     422,    1031,    1032,    1061,    1060,    1082,    1083,    1112,    1111
+     423,    1032,    1033,    1062,    1061,    1083,    1084,    1113,    1112
+     424,    1033,    1034,    1044,    1062,    1084,    1085,    1095,    1113
+     425,    1045,    1046,    1060,    1061,    1096,    1097,    1111,    1112
+     426,    1061,    1062,    1044,    1045,    1112,    1113,    1095,    1096
+     427,    1013,    1014,    1063,    1052,    1064,    1065,    1114,    1103
+     428,    1015,    1058,    1063,    1014,    1066,    1109,    1114,    1065
+     429,    1049,    1050,    1063,    1058,    1100,    1101,    1114,    1109
+     430,    1050,    1051,    1052,    1063,    1101,    1102,    1103,    1114
+     431,     415,     846,     962,     536,     414,     845,    1013,     535
+     432,     846,     857,     963,     962,     845,     856,    1014,    1013
+     433,     857,     868,     964,     963,     856,     867,    1015,    1014
+     434,     868,     879,     965,     964,     867,     878,    1016,    1015
+     435,     879,     890,     966,     965,     878,     889,    1017,    1016
+     436,     890,     901,     967,     966,     889,     900,    1018,    1017
+     437,     901,     912,     968,     967,     900,     911,    1019,    1018
+     438,     912,     923,     969,     968,     911,     922,    1020,    1019
+     439,     923,     934,     970,     969,     922,     933,    1021,    1020
+     440,     934,     945,     971,     970,     933,     944,    1022,    1021
+     441,     945,     956,     972,     971,     944,     955,    1023,    1022
+     442,     956,     692,     714,     972,     955,     691,     713,    1023
+     443,     548,     570,     973,     824,     547,     569,    1024,     823
+     444,     570,     581,     974,     973,     569,     580,    1025,    1024
+     445,     581,     592,     975,     974,     580,     591,    1026,    1025
+     446,     592,     603,     976,     975,     591,     602,    1027,    1026
+     447,     603,     614,     977,     976,     602,     613,    1028,    1027
+     448,     614,     625,     978,     977,     613,     624,    1029,    1028
+     449,     625,     636,     979,     978,     624,     635,    1030,    1029
+     450,     636,     647,     980,     979,     635,     646,    1031,    1030
+     451,     647,     658,     981,     980,     646,     657,    1032,    1031
+     452,     658,     669,     982,     981,     657,     668,    1033,    1032
+     453,     669,     680,     983,     982,     668,     679,    1034,    1033
+     454,     680,     392,     426,     983,     679,     391,     425,    1034
+     455,     714,     725,     984,     972,     713,     724,    1035,    1023
+     456,     725,     736,     985,     984,     724,     735,    1036,    1035
+     457,     736,     747,     986,     985,     735,     746,    1037,    1036
+     458,     747,     758,     987,     986,     746,     757,    1038,    1037
+     459,     758,     769,     988,     987,     757,     768,    1039,    1038
+     460,     769,     780,     989,     988,     768,     779,    1040,    1039
+     461,     780,     791,     990,     989,     779,     790,    1041,    1040
+     462,     791,     802,     991,     990,     790,     801,    1042,    1041
+     463,     802,     813,     992,     991,     801,     812,    1043,    1042
+     464,     813,     824,     973,     992,     812,     823,    1024,    1043
+     465,     426,     437,     993,     983,     425,     436,    1044,    1034
+     466,     437,     448,     994,     993,     436,     447,    1045,    1044
+     467,     448,     459,     995,     994,     447,     458,    1046,    1045
+     468,     459,     470,     996,     995,     458,     469,    1047,    1046
+     469,     470,     481,     997,     996,     469,     480,    1048,    1047
+     470,     481,     492,     998,     997,     480,     491,    1049,    1048
+     471,     492,     503,     999,     998,     491,     502,    1050,    1049
+     472,     503,     514,    1000,     999,     502,     513,    1051,    1050
+     473,     514,     525,    1001,    1000,     513,     524,    1052,    1051
+     474,     525,     536,     962,    1001,     524,     535,    1013,    1052
+     475,     149,     380,     977,     978,     150,     381,    1028,    1029
+     476,     380,     369,     976,     977,     381,     370,    1027,    1028
+     477,     369,     358,    1002,     976,     370,     359,    1053,    1027
+     478,     358,     347,     990,    1002,     359,     348,    1041,    1053
+     479,     347,     336,     989,     990,     348,     337,    1040,    1041
+     480,     336,     325,     988,     989,     337,     326,    1039,    1040
+     481,     973,     974,    1003,     992,    1024,    1025,    1054,    1043
+     482,     974,     975,    1004,    1003,    1025,    1026,    1055,    1054
+     483,     975,     976,    1002,    1004,    1026,    1027,    1053,    1055
+     484,     325,     314,     987,     988,     326,     315,    1038,    1039
+     485,    1004,    1002,     990,     991,    1055,    1053,    1041,    1042
+     486,     991,     992,    1003,    1004,    1042,    1043,    1054,    1055
+     487,     314,     303,     986,     987,     315,     304,    1037,    1038
+     488,     986,     303,    1005,     985,    1037,     304,    1056,    1036
+     489,     303,     292,    1006,    1005,     304,     293,    1057,    1056
+     490,     292,     281,     967,    1006,     293,     282,    1018,    1057
+     491,     281,     270,     966,     967,     282,     271,    1017,    1018
+     492,     270,     259,     965,     966,     271,     260,    1016,    1017
+     493,     259,     248,     964,     965,     260,     249,    1015,    1016
+     494,     967,     968,     969,    1006,    1018,    1019,    1020,    1057
+     495,     248,     237,    1007,     964,     249,     238,    1058,    1015
+     496,     237,     226,     998,    1007,     238,     227,    1049,    1058
+     497,     226,     215,     997,     998,     227,     216,    1048,    1049
+     498,     215,     204,     996,     997,     216,     205,    1047,    1048
+     499,    1006,     969,     970,    1005,    1057,    1020,    1021,    1056
+     500,     204,     193,     995,     996,     205,     194,    1046,    1047
+     501,     970,     971,    1008,    1005,    1021,    1022,    1059,    1056
+     502,     972,     984,    1008,     971,    1023,    1035,    1059,    1022
+     503,     985,    1005,    1008,     984,    1036,    1056,    1059,    1035
+     504,     193,     182,    1009,     995,     194,     183,    1060,    1046
+     505,     182,     171,     980,    1009,     183,     172,    1031,    1060
+     506,     171,     160,     979,     980,     172,     161,    1030,    1031
+     507,     979,     160,     149,     978,    1030,     161,     150,    1029
+     508,     980,     981,    1010,    1009,    1031,    1032,    1061,    1060
+     509,     981,     982,    1011,    1010,    1032,    1033,    1062,    1061
+     510,     982,     983,     993,    1011,    1033,    1034,    1044,    1062
+     511,     994,     995,    1009,    1010,    1045,    1046,    1060,    1061
+     512,    1010,    1011,     993,     994,    1061,    1062,    1044,    1045
+     513,     962,     963,    1012,    1001,    1013,    1014,    1063,    1052
+     514,     964,    1007,    1012,     963,    1015,    1058,    1063,    1014
+     515,     998,     999,    1012,    1007,    1049,    1050,    1063,    1058
+     516,     999,    1000,    1001,    1012,    1050,    1051,    1052,    1063
+     517,     416,     847,    1268,     537,     415,     846,     962,     536
+     518,     847,     858,    1269,    1268,     846,     857,     963,     962
+     519,     858,     869,    1270,    1269,     857,     868,     964,     963
+     520,     869,     880,    1271,    1270,     868,     879,     965,     964
+     521,     880,     891,    1272,    1271,     879,     890,     966,     965
+     522,     891,     902,    1273,    1272,     890,     901,     967,     966
+     523,     902,     913,    1274,    1273,     901,     912,     968,     967
+     524,     913,     924,    1275,    1274,     912,     923,     969,     968
+     525,     924,     935,    1276,    1275,     923,     934,     970,     969
+     526,     935,     946,    1277,    1276,     934,     945,     971,     970
+     527,     946,     957,    1278,    1277,     945,     956,     972,     971
+     528,     957,     693,     715,    1278,     956,     692,     714,     972
+     529,     549,     571,    1279,     825,     548,     570,     973,     824
+     530,     571,     582,    1280,    1279,     570,     581,     974,     973
+     531,     582,     593,    1281,    1280,     581,     592,     975,     974
+     532,     593,     604,    1282,    1281,     592,     603,     976,     975
+     533,     604,     615,    1283,    1282,     603,     614,     977,     976
+     534,     615,     626,    1284,    1283,     614,     625,     978,     977
+     535,     626,     637,    1285,    1284,     625,     636,     979,     978
+     536,     637,     648,    1286,    1285,     636,     647,     980,     979
+     537,     648,     659,    1287,    1286,     647,     658,     981,     980
+     538,     659,     670,    1288,    1287,     658,     669,     982,     981
+     539,     670,     681,    1289,    1288,     669,     680,     983,     982
+     540,     681,     393,     427,    1289,     680,     392,     426,     983
+     541,     715,     726,    1290,    1278,     714,     725,     984,     972
+     542,     726,     737,    1291,    1290,     725,     736,     985,     984
+     543,     737,     748,    1292,    1291,     736,     747,     986,     985
+     544,     748,     759,    1293,    1292,     747,     758,     987,     986
+     545,     759,     770,    1294,    1293,     758,     769,     988,     987
+     546,     770,     781,    1295,    1294,     769,     780,     989,     988
+     547,     781,     792,    1296,    1295,     780,     791,     990,     989
+     548,     792,     803,    1297,    1296,     791,     802,     991,     990
+     549,     803,     814,    1298,    1297,     802,     813,     992,     991
+     550,     814,     825,    1279,    1298,     813,     824,     973,     992
+     551,     427,     438,    1299,    1289,     426,     437,     993,     983
+     552,     438,     449,    1300,    1299,     437,     448,     994,     993
+     553,     449,     460,    1301,    1300,     448,     459,     995,     994
+     554,     460,     471,    1302,    1301,     459,     470,     996,     995
+     555,     471,     482,    1303,    1302,     470,     481,     997,     996
+     556,     482,     493,    1304,    1303,     481,     492,     998,     997
+     557,     493,     504,    1305,    1304,     492,     503,     999,     998
+     558,     504,     515,    1306,    1305,     503,     514,    1000,     999
+     559,     515,     526,    1307,    1306,     514,     525,    1001,    1000
+     560,     526,     537,    1268,    1307,     525,     536,     962,    1001
+     561,     148,     379,    1283,    1284,     149,     380,     977,     978
+     562,     379,     368,    1282,    1283,     380,     369,     976,     977
+     563,     368,     357,    1308,    1282,     369,     358,    1002,     976
+     564,     357,     346,    1296,    1308,     358,     347,     990,    1002
+     565,     346,     335,    1295,    1296,     347,     336,     989,     990
+     566,     335,     324,    1294,    1295,     336,     325,     988,     989
+     567,    1279,    1280,    1309,    1298,     973,     974,    1003,     992
+     568,    1280,    1281,    1310,    1309,     974,     975,    1004,    1003
+     569,    1281,    1282,    1308,    1310,     975,     976,    1002,    1004
+     570,     324,     313,    1293,    1294,     325,     314,     987,     988
+     571,    1310,    1308,    1296,    1297,    1004,    1002,     990,     991
+     572,    1297,    1298,    1309,    1310,     991,     992,    1003,    1004
+     573,     313,     302,    1292,    1293,     314,     303,     986,     987
+     574,    1292,     302,    1311,    1291,     986,     303,    1005,     985
+     575,     302,     291,    1312,    1311,     303,     292,    1006,    1005
+     576,     291,     280,    1273,    1312,     292,     281,     967,    1006
+     577,     280,     269,    1272,    1273,     281,     270,     966,     967
+     578,     269,     258,    1271,    1272,     270,     259,     965,     966
+     579,     258,     247,    1270,    1271,     259,     248,     964,     965
+     580,    1273,    1274,    1275,    1312,     967,     968,     969,    1006
+     581,     247,     236,    1313,    1270,     248,     237,    1007,     964
+     582,     236,     225,    1304,    1313,     237,     226,     998,    1007
+     583,     225,     214,    1303,    1304,     226,     215,     997,     998
+     584,     214,     203,    1302,    1303,     215,     204,     996,     997
+     585,    1312,    1275,    1276,    1311,    1006,     969,     970,    1005
+     586,     203,     192,    1301,    1302,     204,     193,     995,     996
+     587,    1276,    1277,    1314,    1311,     970,     971,    1008,    1005
+     588,    1278,    1290,    1314,    1277,     972,     984,    1008,     971
+     589,    1291,    1311,    1314,    1290,     985,    1005,    1008,     984
+     590,     192,     181,    1315,    1301,     193,     182,    1009,     995
+     591,     181,     170,    1286,    1315,     182,     171,     980,    1009
+     592,     170,     159,    1285,    1286,     171,     160,     979,     980
+     593,    1285,     159,     148,    1284,     979,     160,     149,     978
+     594,    1286,    1287,    1316,    1315,     980,     981,    1010,    1009
+     595,    1287,    1288,    1317,    1316,     981,     982,    1011,    1010
+     596,    1288,    1289,    1299,    1317,     982,     983,     993,    1011
+     597,    1300,    1301,    1315,    1316,     994,     995,    1009,    1010
+     598,    1316,    1317,    1299,    1300,    1010,    1011,     993,     994
+     599,    1268,    1269,    1318,    1307,     962,     963,    1012,    1001
+     600,    1270,    1313,    1318,    1269,     964,    1007,    1012,     963
+     601,    1304,    1305,    1318,    1313,     998,     999,    1012,    1007
+     602,    1305,    1306,    1307,    1318,     999,    1000,    1001,    1012
+     603,     417,     848,    1319,     538,     416,     847,    1268,     537
+     604,     848,     859,    1320,    1319,     847,     858,    1269,    1268
+     605,     859,     870,    1321,    1320,     858,     869,    1270,    1269
+     606,     870,     881,    1322,    1321,     869,     880,    1271,    1270
+     607,     881,     892,    1323,    1322,     880,     891,    1272,    1271
+     608,     892,     903,    1324,    1323,     891,     902,    1273,    1272
+     609,     903,     914,    1325,    1324,     902,     913,    1274,    1273
+     610,     914,     925,    1326,    1325,     913,     924,    1275,    1274
+     611,     925,     936,    1327,    1326,     924,     935,    1276,    1275
+     612,     936,     947,    1328,    1327,     935,     946,    1277,    1276
+     613,     947,     958,    1329,    1328,     946,     957,    1278,    1277
+     614,     958,     694,     716,    1329,     957,     693,     715,    1278
+     615,     550,     572,    1330,     826,     549,     571,    1279,     825
+     616,     572,     583,    1331,    1330,     571,     582,    1280,    1279
+     617,     583,     594,    1332,    1331,     582,     593,    1281,    1280
+     618,     594,     605,    1333,    1332,     593,     604,    1282,    1281
+     619,     605,     616,    1334,    1333,     604,     615,    1283,    1282
+     620,     616,     627,    1335,    1334,     615,     626,    1284,    1283
+     621,     627,     638,    1336,    1335,     626,     637,    1285,    1284
+     622,     638,     649,    1337,    1336,     637,     648,    1286,    1285
+     623,     649,     660,    1338,    1337,     648,     659,    1287,    1286
+     624,     660,     671,    1339,    1338,     659,     670,    1288,    1287
+     625,     671,     682,    1340,    1339,     670,     681,    1289,    1288
+     626,     682,     394,     428,    1340,     681,     393,     427,    1289
+     627,     716,     727,    1341,    1329,     715,     726,    1290,    1278
+     628,     727,     738,    1342,    1341,     726,     737,    1291,    1290
+     629,     738,     749,    1343,    1342,     737,     748,    1292,    1291
+     630,     749,     760,    1344,    1343,     748,     759,    1293,    1292
+     631,     760,     771,    1345,    1344,     759,     770,    1294,    1293
+     632,     771,     782,    1346,    1345,     770,     781,    1295,    1294
+     633,     782,     793,    1347,    1346,     781,     792,    1296,    1295
+     634,     793,     804,    1348,    1347,     792,     803,    1297,    1296
+     635,     804,     815,    1349,    1348,     803,     814,    1298,    1297
+     636,     815,     826,    1330,    1349,     814,     825,    1279,    1298
+     637,     428,     439,    1350,    1340,     427,     438,    1299,    1289
+     638,     439,     450,    1351,    1350,     438,     449,    1300,    1299
+     639,     450,     461,    1352,    1351,     449,     460,    1301,    1300
+     640,     461,     472,    1353,    1352,     460,     471,    1302,    1301
+     641,     472,     483,    1354,    1353,     471,     482,    1303,    1302
+     642,     483,     494,    1355,    1354,     482,     493,    1304,    1303
+     643,     494,     505,    1356,    1355,     493,     504,    1305,    1304
+     644,     505,     516,    1357,    1356,     504,     515,    1306,    1305
+     645,     516,     527,    1358,    1357,     515,     526,    1307,    1306
+     646,     527,     538,    1319,    1358,     526,     537,    1268,    1307
+     647,     147,     378,    1334,    1335,     148,     379,    1283,    1284
+     648,     378,     367,    1333,    1334,     379,     368,    1282,    1283
+     649,     367,     356,    1359,    1333,     368,     357,    1308,    1282
+     650,     356,     345,    1347,    1359,     357,     346,    1296,    1308
+     651,     345,     334,    1346,    1347,     346,     335,    1295,    1296
+     652,     334,     323,    1345,    1346,     335,     324,    1294,    1295
+     653,    1330,    1331,    1360,    1349,    1279,    1280,    1309,    1298
+     654,    1331,    1332,    1361,    1360,    1280,    1281,    1310,    1309
+     655,    1332,    1333,    1359,    1361,    1281,    1282,    1308,    1310
+     656,     323,     312,    1344,    1345,     324,     313,    1293,    1294
+     657,    1361,    1359,    1347,    1348,    1310,    1308,    1296,    1297
+     658,    1348,    1349,    1360,    1361,    1297,    1298,    1309,    1310
+     659,     312,     301,    1343,    1344,     313,     302,    1292,    1293
+     660,    1343,     301,    1362,    1342,    1292,     302,    1311,    1291
+     661,     301,     290,    1363,    1362,     302,     291,    1312,    1311
+     662,     290,     279,    1324,    1363,     291,     280,    1273,    1312
+     663,     279,     268,    1323,    1324,     280,     269,    1272,    1273
+     664,     268,     257,    1322,    1323,     269,     258,    1271,    1272
+     665,     257,     246,    1321,    1322,     258,     247,    1270,    1271
+     666,    1324,    1325,    1326,    1363,    1273,    1274,    1275,    1312
+     667,     246,     235,    1364,    1321,     247,     236,    1313,    1270
+     668,     235,     224,    1355,    1364,     236,     225,    1304,    1313
+     669,     224,     213,    1354,    1355,     225,     214,    1303,    1304
+     670,     213,     202,    1353,    1354,     214,     203,    1302,    1303
+     671,    1363,    1326,    1327,    1362,    1312,    1275,    1276,    1311
+     672,     202,     191,    1352,    1353,     203,     192,    1301,    1302
+     673,    1327,    1328,    1365,    1362,    1276,    1277,    1314,    1311
+     674,    1329,    1341,    1365,    1328,    1278,    1290,    1314,    1277
+     675,    1342,    1362,    1365,    1341,    1291,    1311,    1314,    1290
+     676,     191,     180,    1366,    1352,     192,     181,    1315,    1301
+     677,     180,     169,    1337,    1366,     181,     170,    1286,    1315
+     678,     169,     158,    1336,    1337,     170,     159,    1285,    1286
+     679,    1336,     158,     147,    1335,    1285,     159,     148,    1284
+     680,    1337,    1338,    1367,    1366,    1286,    1287,    1316,    1315
+     681,    1338,    1339,    1368,    1367,    1287,    1288,    1317,    1316
+     682,    1339,    1340,    1350,    1368,    1288,    1289,    1299,    1317
+     683,    1351,    1352,    1366,    1367,    1300,    1301,    1315,    1316
+     684,    1367,    1368,    1350,    1351,    1316,    1317,    1299,    1300
+     685,    1319,    1320,    1369,    1358,    1268,    1269,    1318,    1307
+     686,    1321,    1364,    1369,    1320,    1270,    1313,    1318,    1269
+     687,    1355,    1356,    1369,    1364,    1304,    1305,    1318,    1313
+     688,    1356,    1357,    1358,    1369,    1305,    1306,    1307,    1318
+     689,     418,     849,    1370,     539,     417,     848,    1319,     538
+     690,     849,     860,    1371,    1370,     848,     859,    1320,    1319
+     691,     860,     871,    1372,    1371,     859,     870,    1321,    1320
+     692,     871,     882,    1373,    1372,     870,     881,    1322,    1321
+     693,     882,     893,    1374,    1373,     881,     892,    1323,    1322
+     694,     893,     904,    1375,    1374,     892,     903,    1324,    1323
+     695,     904,     915,    1376,    1375,     903,     914,    1325,    1324
+     696,     915,     926,    1377,    1376,     914,     925,    1326,    1325
+     697,     926,     937,    1378,    1377,     925,     936,    1327,    1326
+     698,     937,     948,    1379,    1378,     936,     947,    1328,    1327
+     699,     948,     959,    1380,    1379,     947,     958,    1329,    1328
+     700,     959,     695,     717,    1380,     958,     694,     716,    1329
+     701,     551,     573,    1381,     827,     550,     572,    1330,     826
+     702,     573,     584,    1382,    1381,     572,     583,    1331,    1330
+     703,     584,     595,    1383,    1382,     583,     594,    1332,    1331
+     704,     595,     606,    1384,    1383,     594,     605,    1333,    1332
+     705,     606,     617,    1385,    1384,     605,     616,    1334,    1333
+     706,     617,     628,    1386,    1385,     616,     627,    1335,    1334
+     707,     628,     639,    1387,    1386,     627,     638,    1336,    1335
+     708,     639,     650,    1388,    1387,     638,     649,    1337,    1336
+     709,     650,     661,    1389,    1388,     649,     660,    1338,    1337
+     710,     661,     672,    1390,    1389,     660,     671,    1339,    1338
+     711,     672,     683,    1391,    1390,     671,     682,    1340,    1339
+     712,     683,     395,     429,    1391,     682,     394,     428,    1340
+     713,     717,     728,    1392,    1380,     716,     727,    1341,    1329
+     714,     728,     739,    1393,    1392,     727,     738,    1342,    1341
+     715,     739,     750,    1394,    1393,     738,     749,    1343,    1342
+     716,     750,     761,    1395,    1394,     749,     760,    1344,    1343
+     717,     761,     772,    1396,    1395,     760,     771,    1345,    1344
+     718,     772,     783,    1397,    1396,     771,     782,    1346,    1345
+     719,     783,     794,    1398,    1397,     782,     793,    1347,    1346
+     720,     794,     805,    1399,    1398,     793,     804,    1348,    1347
+     721,     805,     816,    1400,    1399,     804,     815,    1349,    1348
+     722,     816,     827,    1381,    1400,     815,     826,    1330,    1349
+     723,     429,     440,    1401,    1391,     428,     439,    1350,    1340
+     724,     440,     451,    1402,    1401,     439,     450,    1351,    1350
+     725,     451,     462,    1403,    1402,     450,     461,    1352,    1351
+     726,     462,     473,    1404,    1403,     461,     472,    1353,    1352
+     727,     473,     484,    1405,    1404,     472,     483,    1354,    1353
+     728,     484,     495,    1406,    1405,     483,     494,    1355,    1354
+     729,     495,     506,    1407,    1406,     494,     505,    1356,    1355
+     730,     506,     517,    1408,    1407,     505,     516,    1357,    1356
+     731,     517,     528,    1409,    1408,     516,     527,    1358,    1357
+     732,     528,     539,    1370,    1409,     527,     538,    1319,    1358
+     733,     146,     377,    1385,    1386,     147,     378,    1334,    1335
+     734,     377,     366,    1384,    1385,     378,     367,    1333,    1334
+     735,     366,     355,    1410,    1384,     367,     356,    1359,    1333
+     736,     355,     344,    1398,    1410,     356,     345,    1347,    1359
+     737,     344,     333,    1397,    1398,     345,     334,    1346,    1347
+     738,     333,     322,    1396,    1397,     334,     323,    1345,    1346
+     739,    1381,    1382,    1411,    1400,    1330,    1331,    1360,    1349
+     740,    1382,    1383,    1412,    1411,    1331,    1332,    1361,    1360
+     741,    1383,    1384,    1410,    1412,    1332,    1333,    1359,    1361
+     742,     322,     311,    1395,    1396,     323,     312,    1344,    1345
+     743,    1412,    1410,    1398,    1399,    1361,    1359,    1347,    1348
+     744,    1399,    1400,    1411,    1412,    1348,    1349,    1360,    1361
+     745,     311,     300,    1394,    1395,     312,     301,    1343,    1344
+     746,    1394,     300,    1413,    1393,    1343,     301,    1362,    1342
+     747,     300,     289,    1414,    1413,     301,     290,    1363,    1362
+     748,     289,     278,    1375,    1414,     290,     279,    1324,    1363
+     749,     278,     267,    1374,    1375,     279,     268,    1323,    1324
+     750,     267,     256,    1373,    1374,     268,     257,    1322,    1323
+     751,     256,     245,    1372,    1373,     257,     246,    1321,    1322
+     752,    1375,    1376,    1377,    1414,    1324,    1325,    1326,    1363
+     753,     245,     234,    1415,    1372,     246,     235,    1364,    1321
+     754,     234,     223,    1406,    1415,     235,     224,    1355,    1364
+     755,     223,     212,    1405,    1406,     224,     213,    1354,    1355
+     756,     212,     201,    1404,    1405,     213,     202,    1353,    1354
+     757,    1414,    1377,    1378,    1413,    1363,    1326,    1327,    1362
+     758,     201,     190,    1403,    1404,     202,     191,    1352,    1353
+     759,    1378,    1379,    1416,    1413,    1327,    1328,    1365,    1362
+     760,    1380,    1392,    1416,    1379,    1329,    1341,    1365,    1328
+     761,    1393,    1413,    1416,    1392,    1342,    1362,    1365,    1341
+     762,     190,     179,    1417,    1403,     191,     180,    1366,    1352
+     763,     179,     168,    1388,    1417,     180,     169,    1337,    1366
+     764,     168,     157,    1387,    1388,     169,     158,    1336,    1337
+     765,    1387,     157,     146,    1386,    1336,     158,     147,    1335
+     766,    1388,    1389,    1418,    1417,    1337,    1338,    1367,    1366
+     767,    1389,    1390,    1419,    1418,    1338,    1339,    1368,    1367
+     768,    1390,    1391,    1401,    1419,    1339,    1340,    1350,    1368
+     769,    1402,    1403,    1417,    1418,    1351,    1352,    1366,    1367
+     770,    1418,    1419,    1401,    1402,    1367,    1368,    1350,    1351
+     771,    1370,    1371,    1420,    1409,    1319,    1320,    1369,    1358
+     772,    1372,    1415,    1420,    1371,    1321,    1364,    1369,    1320
+     773,    1406,    1407,    1420,    1415,    1355,    1356,    1369,    1364
+     774,    1407,    1408,    1409,    1420,    1356,    1357,    1358,    1369
+     775,     419,     850,    1421,     540,     418,     849,    1370,     539
+     776,     850,     861,    1422,    1421,     849,     860,    1371,    1370
+     777,     861,     872,    1423,    1422,     860,     871,    1372,    1371
+     778,     872,     883,    1424,    1423,     871,     882,    1373,    1372
+     779,     883,     894,    1425,    1424,     882,     893,    1374,    1373
+     780,     894,     905,    1426,    1425,     893,     904,    1375,    1374
+     781,     905,     916,    1427,    1426,     904,     915,    1376,    1375
+     782,     916,     927,    1428,    1427,     915,     926,    1377,    1376
+     783,     927,     938,    1429,    1428,     926,     937,    1378,    1377
+     784,     938,     949,    1430,    1429,     937,     948,    1379,    1378
+     785,     949,     960,    1431,    1430,     948,     959,    1380,    1379
+     786,     960,     696,     718,    1431,     959,     695,     717,    1380
+     787,     552,     574,    1432,     828,     551,     573,    1381,     827
+     788,     574,     585,    1433,    1432,     573,     584,    1382,    1381
+     789,     585,     596,    1434,    1433,     584,     595,    1383,    1382
+     790,     596,     607,    1435,    1434,     595,     606,    1384,    1383
+     791,     607,     618,    1436,    1435,     606,     617,    1385,    1384
+     792,     618,     629,    1437,    1436,     617,     628,    1386,    1385
+     793,     629,     640,    1438,    1437,     628,     639,    1387,    1386
+     794,     640,     651,    1439,    1438,     639,     650,    1388,    1387
+     795,     651,     662,    1440,    1439,     650,     661,    1389,    1388
+     796,     662,     673,    1441,    1440,     661,     672,    1390,    1389
+     797,     673,     684,    1442,    1441,     672,     683,    1391,    1390
+     798,     684,     396,     430,    1442,     683,     395,     429,    1391
+     799,     718,     729,    1443,    1431,     717,     728,    1392,    1380
+     800,     729,     740,    1444,    1443,     728,     739,    1393,    1392
+     801,     740,     751,    1445,    1444,     739,     750,    1394,    1393
+     802,     751,     762,    1446,    1445,     750,     761,    1395,    1394
+     803,     762,     773,    1447,    1446,     761,     772,    1396,    1395
+     804,     773,     784,    1448,    1447,     772,     783,    1397,    1396
+     805,     784,     795,    1449,    1448,     783,     794,    1398,    1397
+     806,     795,     806,    1450,    1449,     794,     805,    1399,    1398
+     807,     806,     817,    1451,    1450,     805,     816,    1400,    1399
+     808,     817,     828,    1432,    1451,     816,     827,    1381,    1400
+     809,     430,     441,    1452,    1442,     429,     440,    1401,    1391
+     810,     441,     452,    1453,    1452,     440,     451,    1402,    1401
+     811,     452,     463,    1454,    1453,     451,     462,    1403,    1402
+     812,     463,     474,    1455,    1454,     462,     473,    1404,    1403
+     813,     474,     485,    1456,    1455,     473,     484,    1405,    1404
+     814,     485,     496,    1457,    1456,     484,     495,    1406,    1405
+     815,     496,     507,    1458,    1457,     495,     506,    1407,    1406
+     816,     507,     518,    1459,    1458,     506,     517,    1408,    1407
+     817,     518,     529,    1460,    1459,     517,     528,    1409,    1408
+     818,     529,     540,    1421,    1460,     528,     539,    1370,    1409
+     819,     145,     376,    1436,    1437,     146,     377,    1385,    1386
+     820,     376,     365,    1435,    1436,     377,     366,    1384,    1385
+     821,     365,     354,    1461,    1435,     366,     355,    1410,    1384
+     822,     354,     343,    1449,    1461,     355,     344,    1398,    1410
+     823,     343,     332,    1448,    1449,     344,     333,    1397,    1398
+     824,     332,     321,    1447,    1448,     333,     322,    1396,    1397
+     825,    1432,    1433,    1462,    1451,    1381,    1382,    1411,    1400
+     826,    1433,    1434,    1463,    1462,    1382,    1383,    1412,    1411
+     827,    1434,    1435,    1461,    1463,    1383,    1384,    1410,    1412
+     828,     321,     310,    1446,    1447,     322,     311,    1395,    1396
+     829,    1463,    1461,    1449,    1450,    1412,    1410,    1398,    1399
+     830,    1450,    1451,    1462,    1463,    1399,    1400,    1411,    1412
+     831,     310,     299,    1445,    1446,     311,     300,    1394,    1395
+     832,    1445,     299,    1464,    1444,    1394,     300,    1413,    1393
+     833,     299,     288,    1465,    1464,     300,     289,    1414,    1413
+     834,     288,     277,    1426,    1465,     289,     278,    1375,    1414
+     835,     277,     266,    1425,    1426,     278,     267,    1374,    1375
+     836,     266,     255,    1424,    1425,     267,     256,    1373,    1374
+     837,     255,     244,    1423,    1424,     256,     245,    1372,    1373
+     838,    1426,    1427,    1428,    1465,    1375,    1376,    1377,    1414
+     839,     244,     233,    1466,    1423,     245,     234,    1415,    1372
+     840,     233,     222,    1457,    1466,     234,     223,    1406,    1415
+     841,     222,     211,    1456,    1457,     223,     212,    1405,    1406
+     842,     211,     200,    1455,    1456,     212,     201,    1404,    1405
+     843,    1465,    1428,    1429,    1464,    1414,    1377,    1378,    1413
+     844,     200,     189,    1454,    1455,     201,     190,    1403,    1404
+     845,    1429,    1430,    1467,    1464,    1378,    1379,    1416,    1413
+     846,    1431,    1443,    1467,    1430,    1380,    1392,    1416,    1379
+     847,    1444,    1464,    1467,    1443,    1393,    1413,    1416,    1392
+     848,     189,     178,    1468,    1454,     190,     179,    1417,    1403
+     849,     178,     167,    1439,    1468,     179,     168,    1388,    1417
+     850,     167,     156,    1438,    1439,     168,     157,    1387,    1388
+     851,    1438,     156,     145,    1437,    1387,     157,     146,    1386
+     852,    1439,    1440,    1469,    1468,    1388,    1389,    1418,    1417
+     853,    1440,    1441,    1470,    1469,    1389,    1390,    1419,    1418
+     854,    1441,    1442,    1452,    1470,    1390,    1391,    1401,    1419
+     855,    1453,    1454,    1468,    1469,    1402,    1403,    1417,    1418
+     856,    1469,    1470,    1452,    1453,    1418,    1419,    1401,    1402
+     857,    1421,    1422,    1471,    1460,    1370,    1371,    1420,    1409
+     858,    1423,    1466,    1471,    1422,    1372,    1415,    1420,    1371
+     859,    1457,    1458,    1471,    1466,    1406,    1407,    1420,    1415
+     860,    1458,    1459,    1460,    1471,    1407,    1408,    1409,    1420
+     861,     420,     851,    1472,     541,     419,     850,    1421,     540
+     862,     851,     862,    1473,    1472,     850,     861,    1422,    1421
+     863,     862,     873,    1474,    1473,     861,     872,    1423,    1422
+     864,     873,     884,    1475,    1474,     872,     883,    1424,    1423
+     865,     884,     895,    1476,    1475,     883,     894,    1425,    1424
+     866,     895,     906,    1477,    1476,     894,     905,    1426,    1425
+     867,     906,     917,    1478,    1477,     905,     916,    1427,    1426
+     868,     917,     928,    1479,    1478,     916,     927,    1428,    1427
+     869,     928,     939,    1480,    1479,     927,     938,    1429,    1428
+     870,     939,     950,    1481,    1480,     938,     949,    1430,    1429
+     871,     950,     961,    1482,    1481,     949,     960,    1431,    1430
+     872,     961,     697,     719,    1482,     960,     696,     718,    1431
+     873,     553,     575,    1483,     829,     552,     574,    1432,     828
+     874,     575,     586,    1484,    1483,     574,     585,    1433,    1432
+     875,     586,     597,    1485,    1484,     585,     596,    1434,    1433
+     876,     597,     608,    1486,    1485,     596,     607,    1435,    1434
+     877,     608,     619,    1487,    1486,     607,     618,    1436,    1435
+     878,     619,     630,    1488,    1487,     618,     629,    1437,    1436
+     879,     630,     641,    1489,    1488,     629,     640,    1438,    1437
+     880,     641,     652,    1490,    1489,     640,     651,    1439,    1438
+     881,     652,     663,    1491,    1490,     651,     662,    1440,    1439
+     882,     663,     674,    1492,    1491,     662,     673,    1441,    1440
+     883,     674,     685,    1493,    1492,     673,     684,    1442,    1441
+     884,     685,     397,     431,    1493,     684,     396,     430,    1442
+     885,     719,     730,    1494,    1482,     718,     729,    1443,    1431
+     886,     730,     741,    1495,    1494,     729,     740,    1444,    1443
+     887,     741,     752,    1496,    1495,     740,     751,    1445,    1444
+     888,     752,     763,    1497,    1496,     751,     762,    1446,    1445
+     889,     763,     774,    1498,    1497,     762,     773,    1447,    1446
+     890,     774,     785,    1499,    1498,     773,     784,    1448,    1447
+     891,     785,     796,    1500,    1499,     784,     795,    1449,    1448
+     892,     796,     807,    1501,    1500,     795,     806,    1450,    1449
+     893,     807,     818,    1502,    1501,     806,     817,    1451,    1450
+     894,     818,     829,    1483,    1502,     817,     828,    1432,    1451
+     895,     431,     442,    1503,    1493,     430,     441,    1452,    1442
+     896,     442,     453,    1504,    1503,     441,     452,    1453,    1452
+     897,     453,     464,    1505,    1504,     452,     463,    1454,    1453
+     898,     464,     475,    1506,    1505,     463,     474,    1455,    1454
+     899,     475,     486,    1507,    1506,     474,     485,    1456,    1455
+     900,     486,     497,    1508,    1507,     485,     496,    1457,    1456
+     901,     497,     508,    1509,    1508,     496,     507,    1458,    1457
+     902,     508,     519,    1510,    1509,     507,     518,    1459,    1458
+     903,     519,     530,    1511,    1510,     518,     529,    1460,    1459
+     904,     530,     541,    1472,    1511,     529,     540,    1421,    1460
+     905,     144,     375,    1487,    1488,     145,     376,    1436,    1437
+     906,     375,     364,    1486,    1487,     376,     365,    1435,    1436
+     907,     364,     353,    1512,    1486,     365,     354,    1461,    1435
+     908,     353,     342,    1500,    1512,     354,     343,    1449,    1461
+     909,     342,     331,    1499,    1500,     343,     332,    1448,    1449
+     910,     331,     320,    1498,    1499,     332,     321,    1447,    1448
+     911,    1483,    1484,    1513,    1502,    1432,    1433,    1462,    1451
+     912,    1484,    1485,    1514,    1513,    1433,    1434,    1463,    1462
+     913,    1485,    1486,    1512,    1514,    1434,    1435,    1461,    1463
+     914,     320,     309,    1497,    1498,     321,     310,    1446,    1447
+     915,    1514,    1512,    1500,    1501,    1463,    1461,    1449,    1450
+     916,    1501,    1502,    1513,    1514,    1450,    1451,    1462,    1463
+     917,     309,     298,    1496,    1497,     310,     299,    1445,    1446
+     918,    1496,     298,    1515,    1495,    1445,     299,    1464,    1444
+     919,     298,     287,    1516,    1515,     299,     288,    1465,    1464
+     920,     287,     276,    1477,    1516,     288,     277,    1426,    1465
+     921,     276,     265,    1476,    1477,     277,     266,    1425,    1426
+     922,     265,     254,    1475,    1476,     266,     255,    1424,    1425
+     923,     254,     243,    1474,    1475,     255,     244,    1423,    1424
+     924,    1477,    1478,    1479,    1516,    1426,    1427,    1428,    1465
+     925,     243,     232,    1517,    1474,     244,     233,    1466,    1423
+     926,     232,     221,    1508,    1517,     233,     222,    1457,    1466
+     927,     221,     210,    1507,    1508,     222,     211,    1456,    1457
+     928,     210,     199,    1506,    1507,     211,     200,    1455,    1456
+     929,    1516,    1479,    1480,    1515,    1465,    1428,    1429,    1464
+     930,     199,     188,    1505,    1506,     200,     189,    1454,    1455
+     931,    1480,    1481,    1518,    1515,    1429,    1430,    1467,    1464
+     932,    1482,    1494,    1518,    1481,    1431,    1443,    1467,    1430
+     933,    1495,    1515,    1518,    1494,    1444,    1464,    1467,    1443
+     934,     188,     177,    1519,    1505,     189,     178,    1468,    1454
+     935,     177,     166,    1490,    1519,     178,     167,    1439,    1468
+     936,     166,     155,    1489,    1490,     167,     156,    1438,    1439
+     937,    1489,     155,     144,    1488,    1438,     156,     145,    1437
+     938,    1490,    1491,    1520,    1519,    1439,    1440,    1469,    1468
+     939,    1491,    1492,    1521,    1520,    1440,    1441,    1470,    1469
+     940,    1492,    1493,    1503,    1521,    1441,    1442,    1452,    1470
+     941,    1504,    1505,    1519,    1520,    1453,    1454,    1468,    1469
+     942,    1520,    1521,    1503,    1504,    1469,    1470,    1452,    1453
+     943,    1472,    1473,    1522,    1511,    1421,    1422,    1471,    1460
+     944,    1474,    1517,    1522,    1473,    1423,    1466,    1471,    1422
+     945,    1508,    1509,    1522,    1517,    1457,    1458,    1471,    1466
+     946,    1509,    1510,    1511,    1522,    1458,    1459,    1460,    1471
+     947,     398,     840,    1523,     399,     420,     851,    1472,     541
+     948,     840,     839,    1524,    1523,     851,     862,    1473,    1472
+     949,     839,     838,    1525,    1524,     862,     873,    1474,    1473
+     950,     838,     837,    1526,    1525,     873,     884,    1475,    1474
+     951,     837,     836,    1527,    1526,     884,     895,    1476,    1475
+     952,     836,     835,    1528,    1527,     895,     906,    1477,    1476
+     953,     835,     834,    1529,    1528,     906,     917,    1478,    1477
+     954,     834,     833,    1530,    1529,     917,     928,    1479,    1478
+     955,     833,     832,    1531,    1530,     928,     939,    1480,    1479
+     956,     832,     831,    1532,    1531,     939,     950,    1481,    1480
+     957,     831,     830,    1533,    1532,     950,     961,    1482,    1481
+     958,     830,     686,     708,    1533,     961,     697,     719,    1482
+     959,     542,     564,    1534,     698,     553,     575,    1483,     829
+     960,     564,     563,    1535,    1534,     575,     586,    1484,    1483
+     961,     563,     562,    1536,    1535,     586,     597,    1485,    1484
+     962,     562,     561,    1537,    1536,     597,     608,    1486,    1485
+     963,     561,     560,    1538,    1537,     608,     619,    1487,    1486
+     964,     560,     559,    1539,    1538,     619,     630,    1488,    1487
+     965,     559,     558,    1540,    1539,     630,     641,    1489,    1488
+     966,     558,     557,    1541,    1540,     641,     652,    1490,    1489
+     967,     557,     556,    1542,    1541,     652,     663,    1491,    1490
+     968,     556,     555,    1543,    1542,     663,     674,    1492,    1491
+     969,     555,     554,    1544,    1543,     674,     685,    1493,    1492
+     970,     554,     386,     409,    1544,     685,     397,     431,    1493
+     971,     708,     707,    1545,    1533,     719,     730,    1494,    1482
+     972,     707,     706,    1546,    1545,     730,     741,    1495,    1494
+     973,     706,     705,    1547,    1546,     741,     752,    1496,    1495
+     974,     705,     704,    1548,    1547,     752,     763,    1497,    1496
+     975,     704,     703,    1549,    1548,     763,     774,    1498,    1497
+     976,     703,     702,    1550,    1549,     774,     785,    1499,    1498
+     977,     702,     701,    1551,    1550,     785,     796,    1500,    1499
+     978,     701,     700,    1552,    1551,     796,     807,    1501,    1500
+     979,     700,     699,    1553,    1552,     807,     818,    1502,    1501
+     980,     699,     698,    1534,    1553,     818,     829,    1483,    1502
+     981,     409,     408,    1554,    1544,     431,     442,    1503,    1493
+     982,     408,     407,    1555,    1554,     442,     453,    1504,    1503
+     983,     407,     406,    1556,    1555,     453,     464,    1505,    1504
+     984,     406,     405,    1557,    1556,     464,     475,    1506,    1505
+     985,     405,     404,    1558,    1557,     475,     486,    1507,    1506
+     986,     404,     403,    1559,    1558,     486,     497,    1508,    1507
+     987,     403,     402,    1560,    1559,     497,     508,    1509,    1508
+     988,     402,     401,    1561,    1560,     508,     519,    1510,    1509
+     989,     401,     400,    1562,    1561,     519,     530,    1511,    1510
+     990,     400,     399,    1523,    1562,     530,     541,    1472,    1511
+     991,     122,     143,    1538,    1539,     144,     375,    1487,    1488
+     992,     143,     142,    1537,    1538,     375,     364,    1486,    1487
+     993,     142,     141,    1563,    1537,     364,     353,    1512,    1486
+     994,     141,     140,    1551,    1563,     353,     342,    1500,    1512
+     995,     140,     139,    1550,    1551,     342,     331,    1499,    1500
+     996,     139,     138,    1549,    1550,     331,     320,    1498,    1499
+     997,    1534,    1535,    1564,    1553,    1483,    1484,    1513,    1502
+     998,    1535,    1536,    1565,    1564,    1484,    1485,    1514,    1513
+     999,    1536,    1537,    1563,    1565,    1485,    1486,    1512,    1514
+    1000,     138,     137,    1548,    1549,     320,     309,    1497,    1498
+    1001,    1565,    1563,    1551,    1552,    1514,    1512,    1500,    1501
+    1002,    1552,    1553,    1564,    1565,    1501,    1502,    1513,    1514
+    1003,     137,     136,    1547,    1548,     309,     298,    1496,    1497
+    1004,    1547,     136,    1566,    1546,    1496,     298,    1515,    1495
+    1005,     136,     135,    1567,    1566,     298,     287,    1516,    1515
+    1006,     135,     134,    1528,    1567,     287,     276,    1477,    1516
+    1007,     134,     133,    1527,    1528,     276,     265,    1476,    1477
+    1008,     133,     132,    1526,    1527,     265,     254,    1475,    1476
+    1009,     132,     131,    1525,    1526,     254,     243,    1474,    1475
+    1010,    1528,    1529,    1530,    1567,    1477,    1478,    1479,    1516
+    1011,     131,     130,    1568,    1525,     243,     232,    1517,    1474
+    1012,     130,     129,    1559,    1568,     232,     221,    1508,    1517
+    1013,     129,     128,    1558,    1559,     221,     210,    1507,    1508
+    1014,     128,     127,    1557,    1558,     210,     199,    1506,    1507
+    1015,    1567,    1530,    1531,    1566,    1516,    1479,    1480,    1515
+    1016,     127,     126,    1556,    1557,     199,     188,    1505,    1506
+    1017,    1531,    1532,    1569,    1566,    1480,    1481,    1518,    1515
+    1018,    1533,    1545,    1569,    1532,    1482,    1494,    1518,    1481
+    1019,    1546,    1566,    1569,    1545,    1495,    1515,    1518,    1494
+    1020,     126,     125,    1570,    1556,     188,     177,    1519,    1505
+    1021,     125,     124,    1541,    1570,     177,     166,    1490,    1519
+    1022,     124,     123,    1540,    1541,     166,     155,    1489,    1490
+    1023,    1540,     123,     122,    1539,    1489,     155,     144,    1488
+    1024,    1541,    1542,    1571,    1570,    1490,    1491,    1520,    1519
+    1025,    1542,    1543,    1572,    1571,    1491,    1492,    1521,    1520
+    1026,    1543,    1544,    1554,    1572,    1492,    1493,    1503,    1521
+    1027,    1555,    1556,    1570,    1571,    1504,    1505,    1519,    1520
+    1028,    1571,    1572,    1554,    1555,    1520,    1521,    1503,    1504
+    1029,    1523,    1524,    1573,    1562,    1472,    1473,    1522,    1511
+    1030,    1525,    1568,    1573,    1524,    1474,    1517,    1522,    1473
+    1031,    1559,    1560,    1573,    1568,    1508,    1509,    1522,    1517
+    1032,    1560,    1561,    1562,    1573,    1509,    1510,    1511,    1522
+**
+********************************** P R O P E R T I E S ************************
+*SOLID SECTION, ELSET=EB1, MATERIAL=Default-Steel
+**
+*END PART
+**
+**
+**
+********************************** E N D   P A R T S **********************************
+**
+**
+********************************** A S S E M B L Y ************************************
+**
+*ASSEMBLY, NAME=ASSEMBLY1
+**
+*INSTANCE, NAME=Part-Default_1, PART=Part-Default
+*END INSTANCE
+**
+*END ASSEMBLY
+**
+**
+**
+*MATERIAL, NAME = Default-Steel
+*ELASTIC, TYPE=ISOTROPIC
+2.068000e+05, 2.900000e-01
+*DENSITY
+7.000000e-06
+*CONDUCTIVITY,TYPE=ISO
+4.500000e-02
+*SPECIFIC HEAT
+5.000000e+02
+**
+**
+************************************** H I S T O R Y *************************************
+**
+*PREPRINT
+**
+**************************************** S T E P 1 ***************************************
+*STEP,INC=100,NAME=Default Set
+**
+*STATIC
+1, 1, 1e-05, 1
+**
+**
+**
+**
+*END STEP
diff --git a/example/steps/hole_3d_cubit.jou b/example/steps/hole_3d_cubit.jou
new file mode 100644
index 0000000..d278d3f
--- /dev/null
+++ b/example/steps/hole_3d_cubit.jou
@@ -0,0 +1,10 @@
+#!python
+import os
+
+cubit.cmd('set node constraint on')
+cubit.cmd('brick x 10')
+cubit.cmd('create Cylinder height 12 radius 3')
+cubit.cmd('subtract volume 2  from volume 1')
+cubit.cmd('volume 1 size auto factor 5')
+cubit.cmd('mesh volume 1')
+cubit.cmd("export Abaqus \"%s/hole_3d_cubit.inp\" overwrite cubitids"%(os.environ['HOME'],))
diff --git a/example/steps/hole_3d_gmsh.geo b/example/steps/hole_3d_gmsh.geo
new file mode 100644
index 0000000..f609504
--- /dev/null
+++ b/example/steps/hole_3d_gmsh.geo
@@ -0,0 +1,35 @@
+lc = 1;
+r1 = 5;
+r2 = 3;
+
+Point(1) = {-r1,-r1,-r1,lc};
+Point(2) = { r1,-r1,-r1,lc};
+Point(3) = { r1, r1,-r1,lc};
+Point(4) = {-r1, r1,-r1,lc};
+
+Point(5) = {  0,  0,-r1,lc};
+
+Point(6) = {  0,-r2,-r1,lc};
+Point(7) = {  0, r2,-r1,lc};
+Point(8) = { r2,  0,-r1,lc};
+Point(9) = {-r2,  0,-r1,lc};
+
+Line(1) = {1,2};
+Line(2) = {2,3};
+Line(3) = {3,4};
+Line(4) = {4,1};
+
+Circle(5) = {8, 5, 7};
+Circle(6) = {7, 5, 9};
+Circle(7) = {9, 5, 6};
+Circle(8) = {6, 5, 8};
+
+Line Loop(9)  = {1,2,3,4};
+Line Loop(10) = {5,6,7,8};
+
+Plane Surface(11) = {9, 10};
+Recombine Surface {11};
+
+Extrude {0,0,2*r1} {
+  Surface{11}; Layers{2*r1/lc}; Recombine;
+}
diff --git a/example/steps/hole_3d_gmsh.inp b/example/steps/hole_3d_gmsh.inp
new file mode 100644
index 0000000..fc73f1c
--- /dev/null
+++ b/example/steps/hole_3d_gmsh.inp
@@ -0,0 +1,3448 @@
+*Heading
+ hole_3d_gmsh.inp
+*Node
+1, -5, -5, -5
+2, 5, -5, -5
+3, 5, 5, -5
+4, -5, 5, -5
+5, 0, 0, -5
+6, 0, -3, -5
+7, 0, 3, -5
+8, 3, 0, -5
+9, -3, 0, -5
+10, -5, -5, 5
+11, 5, -5, 5
+12, 5, 5, 5
+13, -5, 5, 5
+14, 3, 0, 5
+15, 0, 0, 5
+16, 0, -3, 5
+17, -3, 0, 5
+18, 0, 3, 5
+19, -3.999999999998766, -5, -5
+20, -3.000000000000863, -5, -5
+21, -2.00000000000407, -5, -5
+22, -1.00000000000724, -5, -5
+23, -6.145306485905166e-12, -5, -5
+24, 0.9999999999950866, -5, -5
+25, 1.999999999996321, -5, -5
+26, 2.999999999997555, -5, -5
+27, 3.999999999998776, -5, -5
+28, 5, -3.999999999998766, -5
+29, 5, -3.000000000000863, -5
+30, 5, -2.00000000000407, -5
+31, 5, -1.00000000000724, -5
+32, 5, -6.145306485905166e-12, -5
+33, 5, 0.9999999999950866, -5
+34, 5, 1.999999999996321, -5
+35, 5, 2.999999999997555, -5
+36, 5, 3.999999999998776, -5
+37, 3.999999999998766, 5, -5
+38, 3.000000000000863, 5, -5
+39, 2.00000000000407, 5, -5
+40, 1.00000000000724, 5, -5
+41, 6.145306485905166e-12, 5, -5
+42, -0.9999999999950866, 5, -5
+43, -1.999999999996321, 5, -5
+44, -2.999999999997555, 5, -5
+45, -3.999999999998776, 5, -5
+46, -5, 3.999999999998766, -5
+47, -5, 3.000000000000863, -5
+48, -5, 2.00000000000407, -5
+49, -5, 1.00000000000724, -5
+50, -5, 6.145306485905166e-12, -5
+51, -5, -0.9999999999950866, -5
+52, -5, -1.999999999996321, -5
+53, -5, -2.999999999997555, -5
+54, -5, -3.999999999998776, -5
+55, 2.897777478867742, 0.7764571353055585, -5
+56, 2.598076211355317, 1.499999999996534, -5
+57, 2.12132034356377, 2.121320343555515, -5
+58, 1.500000000003278, 2.598076211351423, -5
+59, 0.7764571353089083, 2.897777478866844, -5
+60, -0.7764571353055585, 2.897777478867742, -5
+61, -1.499999999996534, 2.598076211355317, -5
+62, -2.121320343555515, 2.12132034356377, -5
+63, -2.598076211351423, 1.500000000003278, -5
+64, -2.897777478866844, 0.7764571353089083, -5
+65, -2.897777478867742, -0.7764571353055585, -5
+66, -2.598076211355317, -1.499999999996534, -5
+67, -2.12132034356377, -2.121320343555515, -5
+68, -1.500000000003278, -2.598076211351423, -5
+69, -0.7764571353089083, -2.897777478866844, -5
+70, 0.7764571353055585, -2.897777478867742, -5
+71, 1.499999999996534, -2.598076211355317, -5
+72, 2.121320343555515, -2.12132034356377, -5
+73, 2.598076211351423, -1.500000000003278, -5
+74, 2.897777478866844, -0.7764571353089083, -5
+75, -3.999999999998766, -5, 5
+76, -3.000000000000863, -5, 5
+77, -2.00000000000407, -5, 5
+78, -1.00000000000724, -5, 5
+79, -6.145306485905166e-12, -5, 5
+80, 0.9999999999950866, -5, 5
+81, 1.999999999996321, -5, 5
+82, 2.999999999997555, -5, 5
+83, 3.999999999998776, -5, 5
+84, 5, -3.999999999998766, 5
+85, 5, -3.000000000000863, 5
+86, 5, -2.00000000000407, 5
+87, 5, -1.00000000000724, 5
+88, 5, -6.145306485905166e-12, 5
+89, 5, 0.9999999999950866, 5
+90, 5, 1.999999999996321, 5
+91, 5, 2.999999999997555, 5
+92, 5, 3.999999999998776, 5
+93, 3.999999999998766, 5, 5
+94, 3.000000000000863, 5, 5
+95, 2.00000000000407, 5, 5
+96, 1.00000000000724, 5, 5
+97, 6.145306485905166e-12, 5, 5
+98, -0.9999999999950866, 5, 5
+99, -1.999999999996321, 5, 5
+100, -2.999999999997555, 5, 5
+101, -3.999999999998776, 5, 5
+102, -5, 3.999999999998766, 5
+103, -5, 3.000000000000863, 5
+104, -5, 2.00000000000407, 5
+105, -5, 1.00000000000724, 5
+106, -5, 6.145306485905166e-12, 5
+107, -5, -0.9999999999950866, 5
+108, -5, -1.999999999996321, 5
+109, -5, -2.999999999997555, 5
+110, -5, -3.999999999998776, 5
+111, 2.897777478866844, -0.7764571353089083, 5
+112, 2.598076211351423, -1.500000000003278, 5
+113, 2.121320343555515, -2.12132034356377, 5
+114, 1.499999999996534, -2.598076211355317, 5
+115, 0.7764571353055585, -2.897777478867742, 5
+116, -0.7764571353089083, -2.897777478866844, 5
+117, -1.500000000003278, -2.598076211351423, 5
+118, -2.12132034356377, -2.121320343555515, 5
+119, -2.598076211355317, -1.499999999996534, 5
+120, -2.897777478867742, -0.7764571353055585, 5
+121, -2.897777478866844, 0.7764571353089083, 5
+122, -2.598076211351423, 1.500000000003278, 5
+123, -2.121320343555515, 2.12132034356377, 5
+124, -1.499999999996534, 2.598076211355317, 5
+125, -0.7764571353055585, 2.897777478867742, 5
+126, 0.7764571353089083, 2.897777478866844, 5
+127, 1.500000000003278, 2.598076211351423, 5
+128, 2.12132034356377, 2.121320343555515, 5
+129, 2.598076211355317, 1.499999999996534, 5
+130, 2.897777478867742, 0.7764571353055585, 5
+131, -5, -5, -4
+132, -5, -5, -3
+133, -5, -5, -2
+134, -5, -5, -1
+135, -5, -5, 0
+136, -5, -5, 1
+137, -5, -5, 2
+138, -5, -5, 3
+139, -5, -5, 4
+140, 5, -5, -4
+141, 5, -5, -3
+142, 5, -5, -2
+143, 5, -5, -1
+144, 5, -5, 0
+145, 5, -5, 1
+146, 5, -5, 2
+147, 5, -5, 3
+148, 5, -5, 4
+149, 5, 5, -4
+150, 5, 5, -3
+151, 5, 5, -2
+152, 5, 5, -1
+153, 5, 5, 0
+154, 5, 5, 1
+155, 5, 5, 2
+156, 5, 5, 3
+157, 5, 5, 4
+158, -5, 5, -4
+159, -5, 5, -3
+160, -5, 5, -2
+161, -5, 5, -1
+162, -5, 5, 0
+163, -5, 5, 1
+164, -5, 5, 2
+165, -5, 5, 3
+166, -5, 5, 4
+167, 3, 0, -4
+168, 3, 0, -3
+169, 3, 0, -2
+170, 3, 0, -1
+171, 3, 0, 0
+172, 3, 0, 1
+173, 3, 0, 2
+174, 3, 0, 3
+175, 3, 0, 4
+176, 0, -3, -4
+177, 0, -3, -3
+178, 0, -3, -2
+179, 0, -3, -1
+180, 0, -3, 0
+181, 0, -3, 1
+182, 0, -3, 2
+183, 0, -3, 3
+184, 0, -3, 4
+185, -3, 0, -4
+186, -3, 0, -3
+187, -3, 0, -2
+188, -3, 0, -1
+189, -3, 0, 0
+190, -3, 0, 1
+191, -3, 0, 2
+192, -3, 0, 3
+193, -3, 0, 4
+194, 0, 3, -4
+195, 0, 3, -3
+196, 0, 3, -2
+197, 0, 3, -1
+198, 0, 3, 0
+199, 0, 3, 1
+200, 0, 3, 2
+201, 0, 3, 3
+202, 0, 3, 4
+203, -3.444356651487717, -3.623314556893697, -5
+204, 3.630770861619578, -3.450375428811415, -5
+205, -3.606724759555615, 3.47764896615217, -5
+206, 3.477858484632714, 3.688746030709118, -5
+207, -4.156399691567252, 1.82670878905137, -5
+208, -1.828813450471918, -4.158942678708136, -5
+209, 4.161732457494641, -1.826702910276453, -5
+210, 1.833578373586338, 4.152877803135805, -5
+211, 1.82855514341317, -4.134124222527332, -5
+212, -4.137261178922145, -1.824225453057445, -5
+213, -1.816315221240793, 4.139580491425663, -5
+214, 4.137830708436139, 1.865232377094907, -5
+215, -0.8324523395574734, -3.513832842847894, -5
+216, 3.514812307625984, -0.8327980029018447, -5
+217, -3.51115251493793, 0.8364778404694678, -5
+218, 0.8323027666034133, 3.510317215568977, -5
+219, 0.8130561776793406, -3.509461792913007, -5
+220, -0.8040879808394579, 3.508799622305474, -5
+221, 3.507637985249148, 0.8416152255540563, -5
+222, -3.510297467295204, -0.8103454998307921, -5
+223, 2.879356235150938, 2.925058052321703, -5
+224, -2.955541855893706, 2.743965685902447, -5
+225, -2.741685532801538, -2.974876072989343, -5
+226, 2.986198151494071, -2.746872692158193, -5
+227, -4.132617430180748, 2.609629903505891, -5
+228, -2.628677771111532, -4.143741570119716, -5
+229, 4.137613268344874, -2.63176582991825, -5
+230, 2.623548302690499, 4.13012267383317, -5
+231, -4.12928603258286, -2.623527906833591, -5
+232, -2.624220664337432, 4.143597264629516, -5
+233, 4.089730028010475, 2.776574452224664, -5
+234, 2.629652458540163, -4.129049284126983, -5
+235, 3.302367905051891, 1.70968032242428, -5
+236, 1.53952226362465, -3.315374538869128, -5
+237, -3.332608471710656, -1.535150946932129, -5
+238, -1.526301238015451, 3.329296275968781, -5
+239, -3.320587535849678, 1.683096625180183, -5
+240, -1.683526668391519, -3.324593751159378, -5
+241, 3.327091738802665, -1.68393626645891, -5
+242, 1.639192452215204, 3.325378675743715, -5
+243, 3.287248188077074, -4.189087367530157, -5
+244, -3.295778441163215, 4.201117225082506, -5
+245, -4.178468025548098, -3.275823178071892, -5
+246, 4.12726588759978, 3.663811975302485, -5
+247, -4.106859944586758, 3.331114243505222, -5
+248, 3.285661280600918, 4.188431731448394, -5
+249, -3.286453411102075, -4.188960608239536, -5
+250, 4.191730151615054, -3.289023590308119, -5
+251, -0.8835688592767325, 4.235974096384401, -5
+252, 0.9057525013633964, -4.243953044530109, -5
+253, -4.241218968046286, -0.9001784922335702, -5
+254, 4.228781092544913, 0.9229902907771372, -5
+255, -0.9143189641212353, -4.23580907643538, -5
+256, 4.236027863443724, -0.9133192803687695, -5
+257, 0.9149732711747496, 4.23204845910478, -5
+258, -4.232840912800372, 0.9176807031613724, -5
+259, 0.008845214798486375, 4.260893647236393, -5
+260, 4.267986468814929, 0.001017592833179522, -5
+261, -0.002629743415697635, -4.266103757267889, -5
+262, -4.262233825920551, 0.005818638193458033, -5
+263, 3.592635884483748, -2.634007018244112e-05, -5
+264, -0.000940883892022749, -3.594535563634333, -5
+265, -3.577141536751654, 0.005642074623536417, -5
+266, 0.00838864987239782, 3.576052457680655, -5
+267, -3.999999999998766, -5, -4
+268, -3.999999999998766, -5, -3
+269, -3.999999999998766, -5, -2
+270, -3.999999999998766, -5, -1
+271, -3.999999999998766, -5, 0
+272, -3.999999999998766, -5, 1
+273, -3.999999999998766, -5, 2
+274, -3.999999999998766, -5, 3
+275, -3.999999999998766, -5, 4
+276, -3.000000000000863, -5, -4
+277, -3.000000000000863, -5, -3
+278, -3.000000000000863, -5, -2
+279, -3.000000000000863, -5, -1
+280, -3.000000000000863, -5, 0
+281, -3.000000000000863, -5, 1
+282, -3.000000000000863, -5, 2
+283, -3.000000000000863, -5, 3
+284, -3.000000000000863, -5, 4
+285, -2.00000000000407, -5, -4
+286, -2.00000000000407, -5, -3
+287, -2.00000000000407, -5, -2
+288, -2.00000000000407, -5, -1
+289, -2.00000000000407, -5, 0
+290, -2.00000000000407, -5, 1
+291, -2.00000000000407, -5, 2
+292, -2.00000000000407, -5, 3
+293, -2.00000000000407, -5, 4
+294, -1.00000000000724, -5, -4
+295, -1.00000000000724, -5, -3
+296, -1.00000000000724, -5, -2
+297, -1.00000000000724, -5, -1
+298, -1.00000000000724, -5, 0
+299, -1.00000000000724, -5, 1
+300, -1.00000000000724, -5, 2
+301, -1.00000000000724, -5, 3
+302, -1.00000000000724, -5, 4
+303, -6.145306485905166e-12, -5, -4
+304, -6.145306485905166e-12, -5, -3
+305, -6.145306485905166e-12, -5, -2
+306, -6.145306485905166e-12, -5, -1
+307, -6.145306485905166e-12, -5, 0
+308, -6.145306485905166e-12, -5, 1
+309, -6.145306485905166e-12, -5, 2
+310, -6.145306485905166e-12, -5, 3
+311, -6.145306485905166e-12, -5, 4
+312, 0.9999999999950866, -5, -4
+313, 0.9999999999950866, -5, -3
+314, 0.9999999999950866, -5, -2
+315, 0.9999999999950866, -5, -1
+316, 0.9999999999950866, -5, 0
+317, 0.9999999999950866, -5, 1
+318, 0.9999999999950866, -5, 2
+319, 0.9999999999950866, -5, 3
+320, 0.9999999999950866, -5, 4
+321, 1.999999999996321, -5, -4
+322, 1.999999999996321, -5, -3
+323, 1.999999999996321, -5, -2
+324, 1.999999999996321, -5, -1
+325, 1.999999999996321, -5, 0
+326, 1.999999999996321, -5, 1
+327, 1.999999999996321, -5, 2
+328, 1.999999999996321, -5, 3
+329, 1.999999999996321, -5, 4
+330, 2.999999999997555, -5, -4
+331, 2.999999999997555, -5, -3
+332, 2.999999999997555, -5, -2
+333, 2.999999999997555, -5, -1
+334, 2.999999999997555, -5, 0
+335, 2.999999999997555, -5, 1
+336, 2.999999999997555, -5, 2
+337, 2.999999999997555, -5, 3
+338, 2.999999999997555, -5, 4
+339, 3.999999999998776, -5, -4
+340, 3.999999999998776, -5, -3
+341, 3.999999999998776, -5, -2
+342, 3.999999999998776, -5, -1
+343, 3.999999999998776, -5, 0
+344, 3.999999999998776, -5, 1
+345, 3.999999999998776, -5, 2
+346, 3.999999999998776, -5, 3
+347, 3.999999999998776, -5, 4
+348, 5, -3.999999999998766, -4
+349, 5, -3.999999999998766, -3
+350, 5, -3.999999999998766, -2
+351, 5, -3.999999999998766, -1
+352, 5, -3.999999999998766, 0
+353, 5, -3.999999999998766, 1
+354, 5, -3.999999999998766, 2
+355, 5, -3.999999999998766, 3
+356, 5, -3.999999999998766, 4
+357, 5, -3.000000000000863, -4
+358, 5, -3.000000000000863, -3
+359, 5, -3.000000000000863, -2
+360, 5, -3.000000000000863, -1
+361, 5, -3.000000000000863, 0
+362, 5, -3.000000000000863, 1
+363, 5, -3.000000000000863, 2
+364, 5, -3.000000000000863, 3
+365, 5, -3.000000000000863, 4
+366, 5, -2.00000000000407, -4
+367, 5, -2.00000000000407, -3
+368, 5, -2.00000000000407, -2
+369, 5, -2.00000000000407, -1
+370, 5, -2.00000000000407, 0
+371, 5, -2.00000000000407, 1
+372, 5, -2.00000000000407, 2
+373, 5, -2.00000000000407, 3
+374, 5, -2.00000000000407, 4
+375, 5, -1.00000000000724, -4
+376, 5, -1.00000000000724, -3
+377, 5, -1.00000000000724, -2
+378, 5, -1.00000000000724, -1
+379, 5, -1.00000000000724, 0
+380, 5, -1.00000000000724, 1
+381, 5, -1.00000000000724, 2
+382, 5, -1.00000000000724, 3
+383, 5, -1.00000000000724, 4
+384, 5, -6.145306485905166e-12, -4
+385, 5, -6.145306485905166e-12, -3
+386, 5, -6.145306485905166e-12, -2
+387, 5, -6.145306485905166e-12, -1
+388, 5, -6.145306485905166e-12, 0
+389, 5, -6.145306485905166e-12, 1
+390, 5, -6.145306485905166e-12, 2
+391, 5, -6.145306485905166e-12, 3
+392, 5, -6.145306485905166e-12, 4
+393, 5, 0.9999999999950866, -4
+394, 5, 0.9999999999950866, -3
+395, 5, 0.9999999999950866, -2
+396, 5, 0.9999999999950866, -1
+397, 5, 0.9999999999950866, 0
+398, 5, 0.9999999999950866, 1
+399, 5, 0.9999999999950866, 2
+400, 5, 0.9999999999950866, 3
+401, 5, 0.9999999999950866, 4
+402, 5, 1.999999999996321, -4
+403, 5, 1.999999999996321, -3
+404, 5, 1.999999999996321, -2
+405, 5, 1.999999999996321, -1
+406, 5, 1.999999999996321, 0
+407, 5, 1.999999999996321, 1
+408, 5, 1.999999999996321, 2
+409, 5, 1.999999999996321, 3
+410, 5, 1.999999999996321, 4
+411, 5, 2.999999999997555, -4
+412, 5, 2.999999999997555, -3
+413, 5, 2.999999999997555, -2
+414, 5, 2.999999999997555, -1
+415, 5, 2.999999999997555, 0
+416, 5, 2.999999999997555, 1
+417, 5, 2.999999999997555, 2
+418, 5, 2.999999999997555, 3
+419, 5, 2.999999999997555, 4
+420, 5, 3.999999999998776, -4
+421, 5, 3.999999999998776, -3
+422, 5, 3.999999999998776, -2
+423, 5, 3.999999999998776, -1
+424, 5, 3.999999999998776, 0
+425, 5, 3.999999999998776, 1
+426, 5, 3.999999999998776, 2
+427, 5, 3.999999999998776, 3
+428, 5, 3.999999999998776, 4
+429, 3.999999999998766, 5, -4
+430, 3.999999999998766, 5, -3
+431, 3.999999999998766, 5, -2
+432, 3.999999999998766, 5, -1
+433, 3.999999999998766, 5, 0
+434, 3.999999999998766, 5, 1
+435, 3.999999999998766, 5, 2
+436, 3.999999999998766, 5, 3
+437, 3.999999999998766, 5, 4
+438, 3.000000000000863, 5, -4
+439, 3.000000000000863, 5, -3
+440, 3.000000000000863, 5, -2
+441, 3.000000000000863, 5, -1
+442, 3.000000000000863, 5, 0
+443, 3.000000000000863, 5, 1
+444, 3.000000000000863, 5, 2
+445, 3.000000000000863, 5, 3
+446, 3.000000000000863, 5, 4
+447, 2.00000000000407, 5, -4
+448, 2.00000000000407, 5, -3
+449, 2.00000000000407, 5, -2
+450, 2.00000000000407, 5, -1
+451, 2.00000000000407, 5, 0
+452, 2.00000000000407, 5, 1
+453, 2.00000000000407, 5, 2
+454, 2.00000000000407, 5, 3
+455, 2.00000000000407, 5, 4
+456, 1.00000000000724, 5, -4
+457, 1.00000000000724, 5, -3
+458, 1.00000000000724, 5, -2
+459, 1.00000000000724, 5, -1
+460, 1.00000000000724, 5, 0
+461, 1.00000000000724, 5, 1
+462, 1.00000000000724, 5, 2
+463, 1.00000000000724, 5, 3
+464, 1.00000000000724, 5, 4
+465, 6.145306485905166e-12, 5, -4
+466, 6.145306485905166e-12, 5, -3
+467, 6.145306485905166e-12, 5, -2
+468, 6.145306485905166e-12, 5, -1
+469, 6.145306485905166e-12, 5, 0
+470, 6.145306485905166e-12, 5, 1
+471, 6.145306485905166e-12, 5, 2
+472, 6.145306485905166e-12, 5, 3
+473, 6.145306485905166e-12, 5, 4
+474, -0.9999999999950866, 5, -4
+475, -0.9999999999950866, 5, -3
+476, -0.9999999999950866, 5, -2
+477, -0.9999999999950866, 5, -1
+478, -0.9999999999950866, 5, 0
+479, -0.9999999999950866, 5, 1
+480, -0.9999999999950866, 5, 2
+481, -0.9999999999950866, 5, 3
+482, -0.9999999999950866, 5, 4
+483, -1.999999999996321, 5, -4
+484, -1.999999999996321, 5, -3
+485, -1.999999999996321, 5, -2
+486, -1.999999999996321, 5, -1
+487, -1.999999999996321, 5, 0
+488, -1.999999999996321, 5, 1
+489, -1.999999999996321, 5, 2
+490, -1.999999999996321, 5, 3
+491, -1.999999999996321, 5, 4
+492, -2.999999999997555, 5, -4
+493, -2.999999999997555, 5, -3
+494, -2.999999999997555, 5, -2
+495, -2.999999999997555, 5, -1
+496, -2.999999999997555, 5, 0
+497, -2.999999999997555, 5, 1
+498, -2.999999999997555, 5, 2
+499, -2.999999999997555, 5, 3
+500, -2.999999999997555, 5, 4
+501, -3.999999999998776, 5, -4
+502, -3.999999999998776, 5, -3
+503, -3.999999999998776, 5, -2
+504, -3.999999999998776, 5, -1
+505, -3.999999999998776, 5, 0
+506, -3.999999999998776, 5, 1
+507, -3.999999999998776, 5, 2
+508, -3.999999999998776, 5, 3
+509, -3.999999999998776, 5, 4
+510, -5, 3.999999999998766, -4
+511, -5, 3.999999999998766, -3
+512, -5, 3.999999999998766, -2
+513, -5, 3.999999999998766, -1
+514, -5, 3.999999999998766, 0
+515, -5, 3.999999999998766, 1
+516, -5, 3.999999999998766, 2
+517, -5, 3.999999999998766, 3
+518, -5, 3.999999999998766, 4
+519, -5, 3.000000000000863, -4
+520, -5, 3.000000000000863, -3
+521, -5, 3.000000000000863, -2
+522, -5, 3.000000000000863, -1
+523, -5, 3.000000000000863, 0
+524, -5, 3.000000000000863, 1
+525, -5, 3.000000000000863, 2
+526, -5, 3.000000000000863, 3
+527, -5, 3.000000000000863, 4
+528, -5, 2.00000000000407, -4
+529, -5, 2.00000000000407, -3
+530, -5, 2.00000000000407, -2
+531, -5, 2.00000000000407, -1
+532, -5, 2.00000000000407, 0
+533, -5, 2.00000000000407, 1
+534, -5, 2.00000000000407, 2
+535, -5, 2.00000000000407, 3
+536, -5, 2.00000000000407, 4
+537, -5, 1.00000000000724, -4
+538, -5, 1.00000000000724, -3
+539, -5, 1.00000000000724, -2
+540, -5, 1.00000000000724, -1
+541, -5, 1.00000000000724, 0
+542, -5, 1.00000000000724, 1
+543, -5, 1.00000000000724, 2
+544, -5, 1.00000000000724, 3
+545, -5, 1.00000000000724, 4
+546, -5, 6.145306485905166e-12, -4
+547, -5, 6.145306485905166e-12, -3
+548, -5, 6.145306485905166e-12, -2
+549, -5, 6.145306485905166e-12, -1
+550, -5, 6.145306485905166e-12, 0
+551, -5, 6.145306485905166e-12, 1
+552, -5, 6.145306485905166e-12, 2
+553, -5, 6.145306485905166e-12, 3
+554, -5, 6.145306485905166e-12, 4
+555, -5, -0.9999999999950866, -4
+556, -5, -0.9999999999950866, -3
+557, -5, -0.9999999999950866, -2
+558, -5, -0.9999999999950866, -1
+559, -5, -0.9999999999950866, 0
+560, -5, -0.9999999999950866, 1
+561, -5, -0.9999999999950866, 2
+562, -5, -0.9999999999950866, 3
+563, -5, -0.9999999999950866, 4
+564, -5, -1.999999999996321, -4
+565, -5, -1.999999999996321, -3
+566, -5, -1.999999999996321, -2
+567, -5, -1.999999999996321, -1
+568, -5, -1.999999999996321, 0
+569, -5, -1.999999999996321, 1
+570, -5, -1.999999999996321, 2
+571, -5, -1.999999999996321, 3
+572, -5, -1.999999999996321, 4
+573, -5, -2.999999999997555, -4
+574, -5, -2.999999999997555, -3
+575, -5, -2.999999999997555, -2
+576, -5, -2.999999999997555, -1
+577, -5, -2.999999999997555, 0
+578, -5, -2.999999999997555, 1
+579, -5, -2.999999999997555, 2
+580, -5, -2.999999999997555, 3
+581, -5, -2.999999999997555, 4
+582, -5, -3.999999999998776, -4
+583, -5, -3.999999999998776, -3
+584, -5, -3.999999999998776, -2
+585, -5, -3.999999999998776, -1
+586, -5, -3.999999999998776, 0
+587, -5, -3.999999999998776, 1
+588, -5, -3.999999999998776, 2
+589, -5, -3.999999999998776, 3
+590, -5, -3.999999999998776, 4
+591, 0.7764571353055585, -2.897777478867742, -4
+592, 0.7764571353055585, -2.897777478867742, -3
+593, 0.7764571353055585, -2.897777478867742, -2
+594, 0.7764571353055585, -2.897777478867742, -1
+595, 0.7764571353055585, -2.897777478867742, 0
+596, 0.7764571353055585, -2.897777478867742, 1
+597, 0.7764571353055585, -2.897777478867742, 2
+598, 0.7764571353055585, -2.897777478867742, 3
+599, 0.7764571353055585, -2.897777478867742, 4
+600, 1.499999999996534, -2.598076211355317, -4
+601, 1.499999999996534, -2.598076211355317, -3
+602, 1.499999999996534, -2.598076211355317, -2
+603, 1.499999999996534, -2.598076211355317, -1
+604, 1.499999999996534, -2.598076211355317, 0
+605, 1.499999999996534, -2.598076211355317, 1
+606, 1.499999999996534, -2.598076211355317, 2
+607, 1.499999999996534, -2.598076211355317, 3
+608, 1.499999999996534, -2.598076211355317, 4
+609, 2.121320343555515, -2.12132034356377, -4
+610, 2.121320343555515, -2.12132034356377, -3
+611, 2.121320343555515, -2.12132034356377, -2
+612, 2.121320343555515, -2.12132034356377, -1
+613, 2.121320343555515, -2.12132034356377, 0
+614, 2.121320343555515, -2.12132034356377, 1
+615, 2.121320343555515, -2.12132034356377, 2
+616, 2.121320343555515, -2.12132034356377, 3
+617, 2.121320343555515, -2.12132034356377, 4
+618, 2.598076211351423, -1.500000000003278, -4
+619, 2.598076211351423, -1.500000000003278, -3
+620, 2.598076211351423, -1.500000000003278, -2
+621, 2.598076211351423, -1.500000000003278, -1
+622, 2.598076211351423, -1.500000000003278, 0
+623, 2.598076211351423, -1.500000000003278, 1
+624, 2.598076211351423, -1.500000000003278, 2
+625, 2.598076211351423, -1.500000000003278, 3
+626, 2.598076211351423, -1.500000000003278, 4
+627, 2.897777478866844, -0.7764571353089083, -4
+628, 2.897777478866844, -0.7764571353089083, -3
+629, 2.897777478866844, -0.7764571353089083, -2
+630, 2.897777478866844, -0.7764571353089083, -1
+631, 2.897777478866844, -0.7764571353089083, 0
+632, 2.897777478866844, -0.7764571353089083, 1
+633, 2.897777478866844, -0.7764571353089083, 2
+634, 2.897777478866844, -0.7764571353089083, 3
+635, 2.897777478866844, -0.7764571353089083, 4
+636, -2.897777478867742, -0.7764571353055585, -4
+637, -2.897777478867742, -0.7764571353055585, -3
+638, -2.897777478867742, -0.7764571353055585, -2
+639, -2.897777478867742, -0.7764571353055585, -1
+640, -2.897777478867742, -0.7764571353055585, 0
+641, -2.897777478867742, -0.7764571353055585, 1
+642, -2.897777478867742, -0.7764571353055585, 2
+643, -2.897777478867742, -0.7764571353055585, 3
+644, -2.897777478867742, -0.7764571353055585, 4
+645, -2.598076211355317, -1.499999999996534, -4
+646, -2.598076211355317, -1.499999999996534, -3
+647, -2.598076211355317, -1.499999999996534, -2
+648, -2.598076211355317, -1.499999999996534, -1
+649, -2.598076211355317, -1.499999999996534, 0
+650, -2.598076211355317, -1.499999999996534, 1
+651, -2.598076211355317, -1.499999999996534, 2
+652, -2.598076211355317, -1.499999999996534, 3
+653, -2.598076211355317, -1.499999999996534, 4
+654, -2.12132034356377, -2.121320343555515, -4
+655, -2.12132034356377, -2.121320343555515, -3
+656, -2.12132034356377, -2.121320343555515, -2
+657, -2.12132034356377, -2.121320343555515, -1
+658, -2.12132034356377, -2.121320343555515, 0
+659, -2.12132034356377, -2.121320343555515, 1
+660, -2.12132034356377, -2.121320343555515, 2
+661, -2.12132034356377, -2.121320343555515, 3
+662, -2.12132034356377, -2.121320343555515, 4
+663, -1.500000000003278, -2.598076211351423, -4
+664, -1.500000000003278, -2.598076211351423, -3
+665, -1.500000000003278, -2.598076211351423, -2
+666, -1.500000000003278, -2.598076211351423, -1
+667, -1.500000000003278, -2.598076211351423, 0
+668, -1.500000000003278, -2.598076211351423, 1
+669, -1.500000000003278, -2.598076211351423, 2
+670, -1.500000000003278, -2.598076211351423, 3
+671, -1.500000000003278, -2.598076211351423, 4
+672, -0.7764571353089083, -2.897777478866844, -4
+673, -0.7764571353089083, -2.897777478866844, -3
+674, -0.7764571353089083, -2.897777478866844, -2
+675, -0.7764571353089083, -2.897777478866844, -1
+676, -0.7764571353089083, -2.897777478866844, 0
+677, -0.7764571353089083, -2.897777478866844, 1
+678, -0.7764571353089083, -2.897777478866844, 2
+679, -0.7764571353089083, -2.897777478866844, 3
+680, -0.7764571353089083, -2.897777478866844, 4
+681, -0.7764571353055585, 2.897777478867742, -4
+682, -0.7764571353055585, 2.897777478867742, -3
+683, -0.7764571353055585, 2.897777478867742, -2
+684, -0.7764571353055585, 2.897777478867742, -1
+685, -0.7764571353055585, 2.897777478867742, 0
+686, -0.7764571353055585, 2.897777478867742, 1
+687, -0.7764571353055585, 2.897777478867742, 2
+688, -0.7764571353055585, 2.897777478867742, 3
+689, -0.7764571353055585, 2.897777478867742, 4
+690, -1.499999999996534, 2.598076211355317, -4
+691, -1.499999999996534, 2.598076211355317, -3
+692, -1.499999999996534, 2.598076211355317, -2
+693, -1.499999999996534, 2.598076211355317, -1
+694, -1.499999999996534, 2.598076211355317, 0
+695, -1.499999999996534, 2.598076211355317, 1
+696, -1.499999999996534, 2.598076211355317, 2
+697, -1.499999999996534, 2.598076211355317, 3
+698, -1.499999999996534, 2.598076211355317, 4
+699, -2.121320343555515, 2.12132034356377, -4
+700, -2.121320343555515, 2.12132034356377, -3
+701, -2.121320343555515, 2.12132034356377, -2
+702, -2.121320343555515, 2.12132034356377, -1
+703, -2.121320343555515, 2.12132034356377, 0
+704, -2.121320343555515, 2.12132034356377, 1
+705, -2.121320343555515, 2.12132034356377, 2
+706, -2.121320343555515, 2.12132034356377, 3
+707, -2.121320343555515, 2.12132034356377, 4
+708, -2.598076211351423, 1.500000000003278, -4
+709, -2.598076211351423, 1.500000000003278, -3
+710, -2.598076211351423, 1.500000000003278, -2
+711, -2.598076211351423, 1.500000000003278, -1
+712, -2.598076211351423, 1.500000000003278, 0
+713, -2.598076211351423, 1.500000000003278, 1
+714, -2.598076211351423, 1.500000000003278, 2
+715, -2.598076211351423, 1.500000000003278, 3
+716, -2.598076211351423, 1.500000000003278, 4
+717, -2.897777478866844, 0.7764571353089083, -4
+718, -2.897777478866844, 0.7764571353089083, -3
+719, -2.897777478866844, 0.7764571353089083, -2
+720, -2.897777478866844, 0.7764571353089083, -1
+721, -2.897777478866844, 0.7764571353089083, 0
+722, -2.897777478866844, 0.7764571353089083, 1
+723, -2.897777478866844, 0.7764571353089083, 2
+724, -2.897777478866844, 0.7764571353089083, 3
+725, -2.897777478866844, 0.7764571353089083, 4
+726, 2.897777478867742, 0.7764571353055585, -4
+727, 2.897777478867742, 0.7764571353055585, -3
+728, 2.897777478867742, 0.7764571353055585, -2
+729, 2.897777478867742, 0.7764571353055585, -1
+730, 2.897777478867742, 0.7764571353055585, 0
+731, 2.897777478867742, 0.7764571353055585, 1
+732, 2.897777478867742, 0.7764571353055585, 2
+733, 2.897777478867742, 0.7764571353055585, 3
+734, 2.897777478867742, 0.7764571353055585, 4
+735, 2.598076211355317, 1.499999999996534, -4
+736, 2.598076211355317, 1.499999999996534, -3
+737, 2.598076211355317, 1.499999999996534, -2
+738, 2.598076211355317, 1.499999999996534, -1
+739, 2.598076211355317, 1.499999999996534, 0
+740, 2.598076211355317, 1.499999999996534, 1
+741, 2.598076211355317, 1.499999999996534, 2
+742, 2.598076211355317, 1.499999999996534, 3
+743, 2.598076211355317, 1.499999999996534, 4
+744, 2.12132034356377, 2.121320343555515, -4
+745, 2.12132034356377, 2.121320343555515, -3
+746, 2.12132034356377, 2.121320343555515, -2
+747, 2.12132034356377, 2.121320343555515, -1
+748, 2.12132034356377, 2.121320343555515, 0
+749, 2.12132034356377, 2.121320343555515, 1
+750, 2.12132034356377, 2.121320343555515, 2
+751, 2.12132034356377, 2.121320343555515, 3
+752, 2.12132034356377, 2.121320343555515, 4
+753, 1.500000000003278, 2.598076211351423, -4
+754, 1.500000000003278, 2.598076211351423, -3
+755, 1.500000000003278, 2.598076211351423, -2
+756, 1.500000000003278, 2.598076211351423, -1
+757, 1.500000000003278, 2.598076211351423, 0
+758, 1.500000000003278, 2.598076211351423, 1
+759, 1.500000000003278, 2.598076211351423, 2
+760, 1.500000000003278, 2.598076211351423, 3
+761, 1.500000000003278, 2.598076211351423, 4
+762, 0.7764571353089083, 2.897777478866844, -4
+763, 0.7764571353089083, 2.897777478866844, -3
+764, 0.7764571353089083, 2.897777478866844, -2
+765, 0.7764571353089083, 2.897777478866844, -1
+766, 0.7764571353089083, 2.897777478866844, 0
+767, 0.7764571353089083, 2.897777478866844, 1
+768, 0.7764571353089083, 2.897777478866844, 2
+769, 0.7764571353089083, 2.897777478866844, 3
+770, 0.7764571353089083, 2.897777478866844, 4
+771, -3.444356651487717, -3.623314556893697, 5
+772, 3.630770861619578, -3.450375428811415, 5
+773, -3.606724759555615, 3.47764896615217, 5
+774, 3.477858484632714, 3.688746030709118, 5
+775, -4.156399691567252, 1.82670878905137, 5
+776, -1.828813450471918, -4.158942678708136, 5
+777, 4.161732457494641, -1.826702910276453, 5
+778, 1.833578373586338, 4.152877803135805, 5
+779, 1.82855514341317, -4.134124222527332, 5
+780, -4.137261178922145, -1.824225453057445, 5
+781, -1.816315221240793, 4.139580491425663, 5
+782, 4.137830708436139, 1.865232377094907, 5
+783, -0.8324523395574734, -3.513832842847894, 5
+784, 3.514812307625984, -0.8327980029018447, 5
+785, -3.51115251493793, 0.8364778404694678, 5
+786, 0.8323027666034133, 3.510317215568977, 5
+787, 0.8130561776793406, -3.509461792913007, 5
+788, -0.8040879808394579, 3.508799622305474, 5
+789, 3.507637985249148, 0.8416152255540563, 5
+790, -3.510297467295204, -0.8103454998307921, 5
+791, 2.879356235150938, 2.925058052321703, 5
+792, -2.955541855893706, 2.743965685902447, 5
+793, -2.741685532801538, -2.974876072989343, 5
+794, 2.986198151494071, -2.746872692158193, 5
+795, -4.132617430180748, 2.609629903505891, 5
+796, -2.628677771111532, -4.143741570119716, 5
+797, 4.137613268344874, -2.63176582991825, 5
+798, 2.623548302690499, 4.13012267383317, 5
+799, -4.12928603258286, -2.623527906833591, 5
+800, -2.624220664337432, 4.143597264629516, 5
+801, 4.089730028010475, 2.776574452224664, 5
+802, 2.629652458540163, -4.129049284126983, 5
+803, 3.302367905051891, 1.70968032242428, 5
+804, 1.53952226362465, -3.315374538869128, 5
+805, -3.332608471710656, -1.535150946932129, 5
+806, -1.526301238015451, 3.329296275968781, 5
+807, -3.320587535849678, 1.683096625180183, 5
+808, -1.683526668391519, -3.324593751159378, 5
+809, 3.327091738802665, -1.68393626645891, 5
+810, 1.639192452215204, 3.325378675743715, 5
+811, 3.287248188077074, -4.189087367530157, 5
+812, -3.295778441163215, 4.201117225082506, 5
+813, -4.178468025548098, -3.275823178071892, 5
+814, 4.12726588759978, 3.663811975302485, 5
+815, -4.106859944586758, 3.331114243505222, 5
+816, 3.285661280600918, 4.188431731448394, 5
+817, -3.286453411102075, -4.188960608239536, 5
+818, 4.191730151615054, -3.289023590308119, 5
+819, -0.8835688592767325, 4.235974096384401, 5
+820, 0.9057525013633964, -4.243953044530109, 5
+821, -4.241218968046286, -0.9001784922335702, 5
+822, 4.228781092544913, 0.9229902907771372, 5
+823, -0.9143189641212353, -4.23580907643538, 5
+824, 4.236027863443724, -0.9133192803687695, 5
+825, 0.9149732711747496, 4.23204845910478, 5
+826, -4.232840912800372, 0.9176807031613724, 5
+827, 0.008845214798486375, 4.260893647236393, 5
+828, 4.267986468814929, 0.001017592833179522, 5
+829, -0.002629743415697635, -4.266103757267889, 5
+830, -4.262233825920551, 0.005818638193458033, 5
+831, 3.592635884483748, -2.634007018244112e-05, 5
+832, -0.000940883892022749, -3.594535563634333, 5
+833, -3.577141536751654, 0.005642074623536417, 5
+834, 0.00838864987239782, 3.576052457680655, 5
+835, -3.444356651487717, -3.623314556893697, -4
+836, -3.444356651487717, -3.623314556893697, -3
+837, -3.444356651487717, -3.623314556893697, -2
+838, -3.444356651487717, -3.623314556893697, -1
+839, -3.444356651487717, -3.623314556893697, 0
+840, -3.444356651487717, -3.623314556893697, 1
+841, -3.444356651487717, -3.623314556893697, 2
+842, -3.444356651487717, -3.623314556893697, 3
+843, -3.444356651487717, -3.623314556893697, 4
+844, 3.630770861619578, -3.450375428811415, -4
+845, 3.630770861619578, -3.450375428811415, -3
+846, 3.630770861619578, -3.450375428811415, -2
+847, 3.630770861619578, -3.450375428811415, -1
+848, 3.630770861619578, -3.450375428811415, 0
+849, 3.630770861619578, -3.450375428811415, 1
+850, 3.630770861619578, -3.450375428811415, 2
+851, 3.630770861619578, -3.450375428811415, 3
+852, 3.630770861619578, -3.450375428811415, 4
+853, -3.606724759555615, 3.47764896615217, -4
+854, -3.606724759555615, 3.47764896615217, -3
+855, -3.606724759555615, 3.47764896615217, -2
+856, -3.606724759555615, 3.47764896615217, -1
+857, -3.606724759555615, 3.47764896615217, 0
+858, -3.606724759555615, 3.47764896615217, 1
+859, -3.606724759555615, 3.47764896615217, 2
+860, -3.606724759555615, 3.47764896615217, 3
+861, -3.606724759555615, 3.47764896615217, 4
+862, 3.477858484632714, 3.688746030709118, -4
+863, 3.477858484632714, 3.688746030709118, -3
+864, 3.477858484632714, 3.688746030709118, -2
+865, 3.477858484632714, 3.688746030709118, -1
+866, 3.477858484632714, 3.688746030709118, 0
+867, 3.477858484632714, 3.688746030709118, 1
+868, 3.477858484632714, 3.688746030709118, 2
+869, 3.477858484632714, 3.688746030709118, 3
+870, 3.477858484632714, 3.688746030709118, 4
+871, -4.156399691567252, 1.82670878905137, -4
+872, -4.156399691567252, 1.82670878905137, -3
+873, -4.156399691567252, 1.82670878905137, -2
+874, -4.156399691567252, 1.82670878905137, -1
+875, -4.156399691567252, 1.82670878905137, 0
+876, -4.156399691567252, 1.82670878905137, 1
+877, -4.156399691567252, 1.82670878905137, 2
+878, -4.156399691567252, 1.82670878905137, 3
+879, -4.156399691567252, 1.82670878905137, 4
+880, -1.828813450471918, -4.158942678708136, -4
+881, -1.828813450471918, -4.158942678708136, -3
+882, -1.828813450471918, -4.158942678708136, -2
+883, -1.828813450471918, -4.158942678708136, -1
+884, -1.828813450471918, -4.158942678708136, 0
+885, -1.828813450471918, -4.158942678708136, 1
+886, -1.828813450471918, -4.158942678708136, 2
+887, -1.828813450471918, -4.158942678708136, 3
+888, -1.828813450471918, -4.158942678708136, 4
+889, 4.161732457494641, -1.826702910276453, -4
+890, 4.161732457494641, -1.826702910276453, -3
+891, 4.161732457494641, -1.826702910276453, -2
+892, 4.161732457494641, -1.826702910276453, -1
+893, 4.161732457494641, -1.826702910276453, 0
+894, 4.161732457494641, -1.826702910276453, 1
+895, 4.161732457494641, -1.826702910276453, 2
+896, 4.161732457494641, -1.826702910276453, 3
+897, 4.161732457494641, -1.826702910276453, 4
+898, 1.833578373586338, 4.152877803135805, -4
+899, 1.833578373586338, 4.152877803135805, -3
+900, 1.833578373586338, 4.152877803135805, -2
+901, 1.833578373586338, 4.152877803135805, -1
+902, 1.833578373586338, 4.152877803135805, 0
+903, 1.833578373586338, 4.152877803135805, 1
+904, 1.833578373586338, 4.152877803135805, 2
+905, 1.833578373586338, 4.152877803135805, 3
+906, 1.833578373586338, 4.152877803135805, 4
+907, 1.82855514341317, -4.134124222527332, -4
+908, 1.82855514341317, -4.134124222527332, -3
+909, 1.82855514341317, -4.134124222527332, -2
+910, 1.82855514341317, -4.134124222527332, -1
+911, 1.82855514341317, -4.134124222527332, 0
+912, 1.82855514341317, -4.134124222527332, 1
+913, 1.82855514341317, -4.134124222527332, 2
+914, 1.82855514341317, -4.134124222527332, 3
+915, 1.82855514341317, -4.134124222527332, 4
+916, -4.137261178922145, -1.824225453057445, -4
+917, -4.137261178922145, -1.824225453057445, -3
+918, -4.137261178922145, -1.824225453057445, -2
+919, -4.137261178922145, -1.824225453057445, -1
+920, -4.137261178922145, -1.824225453057445, 0
+921, -4.137261178922145, -1.824225453057445, 1
+922, -4.137261178922145, -1.824225453057445, 2
+923, -4.137261178922145, -1.824225453057445, 3
+924, -4.137261178922145, -1.824225453057445, 4
+925, -1.816315221240793, 4.139580491425663, -4
+926, -1.816315221240793, 4.139580491425663, -3
+927, -1.816315221240793, 4.139580491425663, -2
+928, -1.816315221240793, 4.139580491425663, -1
+929, -1.816315221240793, 4.139580491425663, 0
+930, -1.816315221240793, 4.139580491425663, 1
+931, -1.816315221240793, 4.139580491425663, 2
+932, -1.816315221240793, 4.139580491425663, 3
+933, -1.816315221240793, 4.139580491425663, 4
+934, 4.137830708436139, 1.865232377094907, -4
+935, 4.137830708436139, 1.865232377094907, -3
+936, 4.137830708436139, 1.865232377094907, -2
+937, 4.137830708436139, 1.865232377094907, -1
+938, 4.137830708436139, 1.865232377094907, 0
+939, 4.137830708436139, 1.865232377094907, 1
+940, 4.137830708436139, 1.865232377094907, 2
+941, 4.137830708436139, 1.865232377094907, 3
+942, 4.137830708436139, 1.865232377094907, 4
+943, -0.8324523395574734, -3.513832842847894, -4
+944, -0.8324523395574734, -3.513832842847894, -3
+945, -0.8324523395574734, -3.513832842847894, -2
+946, -0.8324523395574734, -3.513832842847894, -1
+947, -0.8324523395574734, -3.513832842847894, 0
+948, -0.8324523395574734, -3.513832842847894, 1
+949, -0.8324523395574734, -3.513832842847894, 2
+950, -0.8324523395574734, -3.513832842847894, 3
+951, -0.8324523395574734, -3.513832842847894, 4
+952, 3.514812307625984, -0.8327980029018447, -4
+953, 3.514812307625984, -0.8327980029018447, -3
+954, 3.514812307625984, -0.8327980029018447, -2
+955, 3.514812307625984, -0.8327980029018447, -1
+956, 3.514812307625984, -0.8327980029018447, 0
+957, 3.514812307625984, -0.8327980029018447, 1
+958, 3.514812307625984, -0.8327980029018447, 2
+959, 3.514812307625984, -0.8327980029018447, 3
+960, 3.514812307625984, -0.8327980029018447, 4
+961, -3.51115251493793, 0.8364778404694678, -4
+962, -3.51115251493793, 0.8364778404694678, -3
+963, -3.51115251493793, 0.8364778404694678, -2
+964, -3.51115251493793, 0.8364778404694678, -1
+965, -3.51115251493793, 0.8364778404694678, 0
+966, -3.51115251493793, 0.8364778404694678, 1
+967, -3.51115251493793, 0.8364778404694678, 2
+968, -3.51115251493793, 0.8364778404694678, 3
+969, -3.51115251493793, 0.8364778404694678, 4
+970, 0.8323027666034133, 3.510317215568977, -4
+971, 0.8323027666034133, 3.510317215568977, -3
+972, 0.8323027666034133, 3.510317215568977, -2
+973, 0.8323027666034133, 3.510317215568977, -1
+974, 0.8323027666034133, 3.510317215568977, 0
+975, 0.8323027666034133, 3.510317215568977, 1
+976, 0.8323027666034133, 3.510317215568977, 2
+977, 0.8323027666034133, 3.510317215568977, 3
+978, 0.8323027666034133, 3.510317215568977, 4
+979, 0.8130561776793406, -3.509461792913007, -4
+980, 0.8130561776793406, -3.509461792913007, -3
+981, 0.8130561776793406, -3.509461792913007, -2
+982, 0.8130561776793406, -3.509461792913007, -1
+983, 0.8130561776793406, -3.509461792913007, 0
+984, 0.8130561776793406, -3.509461792913007, 1
+985, 0.8130561776793406, -3.509461792913007, 2
+986, 0.8130561776793406, -3.509461792913007, 3
+987, 0.8130561776793406, -3.509461792913007, 4
+988, -0.8040879808394579, 3.508799622305474, -4
+989, -0.8040879808394579, 3.508799622305474, -3
+990, -0.8040879808394579, 3.508799622305474, -2
+991, -0.8040879808394579, 3.508799622305474, -1
+992, -0.8040879808394579, 3.508799622305474, 0
+993, -0.8040879808394579, 3.508799622305474, 1
+994, -0.8040879808394579, 3.508799622305474, 2
+995, -0.8040879808394579, 3.508799622305474, 3
+996, -0.8040879808394579, 3.508799622305474, 4
+997, 3.507637985249148, 0.8416152255540563, -4
+998, 3.507637985249148, 0.8416152255540563, -3
+999, 3.507637985249148, 0.8416152255540563, -2
+1000, 3.507637985249148, 0.8416152255540563, -1
+1001, 3.507637985249148, 0.8416152255540563, 0
+1002, 3.507637985249148, 0.8416152255540563, 1
+1003, 3.507637985249148, 0.8416152255540563, 2
+1004, 3.507637985249148, 0.8416152255540563, 3
+1005, 3.507637985249148, 0.8416152255540563, 4
+1006, -3.510297467295204, -0.8103454998307921, -4
+1007, -3.510297467295204, -0.8103454998307921, -3
+1008, -3.510297467295204, -0.8103454998307921, -2
+1009, -3.510297467295204, -0.8103454998307921, -1
+1010, -3.510297467295204, -0.8103454998307921, 0
+1011, -3.510297467295204, -0.8103454998307921, 1
+1012, -3.510297467295204, -0.8103454998307921, 2
+1013, -3.510297467295204, -0.8103454998307921, 3
+1014, -3.510297467295204, -0.8103454998307921, 4
+1015, 2.879356235150938, 2.925058052321703, -4
+1016, 2.879356235150938, 2.925058052321703, -3
+1017, 2.879356235150938, 2.925058052321703, -2
+1018, 2.879356235150938, 2.925058052321703, -1
+1019, 2.879356235150938, 2.925058052321703, 0
+1020, 2.879356235150938, 2.925058052321703, 1
+1021, 2.879356235150938, 2.925058052321703, 2
+1022, 2.879356235150938, 2.925058052321703, 3
+1023, 2.879356235150938, 2.925058052321703, 4
+1024, -2.955541855893706, 2.743965685902447, -4
+1025, -2.955541855893706, 2.743965685902447, -3
+1026, -2.955541855893706, 2.743965685902447, -2
+1027, -2.955541855893706, 2.743965685902447, -1
+1028, -2.955541855893706, 2.743965685902447, 0
+1029, -2.955541855893706, 2.743965685902447, 1
+1030, -2.955541855893706, 2.743965685902447, 2
+1031, -2.955541855893706, 2.743965685902447, 3
+1032, -2.955541855893706, 2.743965685902447, 4
+1033, -2.741685532801538, -2.974876072989343, -4
+1034, -2.741685532801538, -2.974876072989343, -3
+1035, -2.741685532801538, -2.974876072989343, -2
+1036, -2.741685532801538, -2.974876072989343, -1
+1037, -2.741685532801538, -2.974876072989343, 0
+1038, -2.741685532801538, -2.974876072989343, 1
+1039, -2.741685532801538, -2.974876072989343, 2
+1040, -2.741685532801538, -2.974876072989343, 3
+1041, -2.741685532801538, -2.974876072989343, 4
+1042, 2.986198151494071, -2.746872692158193, -4
+1043, 2.986198151494071, -2.746872692158193, -3
+1044, 2.986198151494071, -2.746872692158193, -2
+1045, 2.986198151494071, -2.746872692158193, -1
+1046, 2.986198151494071, -2.746872692158193, 0
+1047, 2.986198151494071, -2.746872692158193, 1
+1048, 2.986198151494071, -2.746872692158193, 2
+1049, 2.986198151494071, -2.746872692158193, 3
+1050, 2.986198151494071, -2.746872692158193, 4
+1051, -4.132617430180748, 2.609629903505891, -4
+1052, -4.132617430180748, 2.609629903505891, -3
+1053, -4.132617430180748, 2.609629903505891, -2
+1054, -4.132617430180748, 2.609629903505891, -1
+1055, -4.132617430180748, 2.609629903505891, 0
+1056, -4.132617430180748, 2.609629903505891, 1
+1057, -4.132617430180748, 2.609629903505891, 2
+1058, -4.132617430180748, 2.609629903505891, 3
+1059, -4.132617430180748, 2.609629903505891, 4
+1060, -2.628677771111532, -4.143741570119716, -4
+1061, -2.628677771111532, -4.143741570119716, -3
+1062, -2.628677771111532, -4.143741570119716, -2
+1063, -2.628677771111532, -4.143741570119716, -1
+1064, -2.628677771111532, -4.143741570119716, 0
+1065, -2.628677771111532, -4.143741570119716, 1
+1066, -2.628677771111532, -4.143741570119716, 2
+1067, -2.628677771111532, -4.143741570119716, 3
+1068, -2.628677771111532, -4.143741570119716, 4
+1069, 4.137613268344874, -2.63176582991825, -4
+1070, 4.137613268344874, -2.63176582991825, -3
+1071, 4.137613268344874, -2.63176582991825, -2
+1072, 4.137613268344874, -2.63176582991825, -1
+1073, 4.137613268344874, -2.63176582991825, 0
+1074, 4.137613268344874, -2.63176582991825, 1
+1075, 4.137613268344874, -2.63176582991825, 2
+1076, 4.137613268344874, -2.63176582991825, 3
+1077, 4.137613268344874, -2.63176582991825, 4
+1078, 2.623548302690499, 4.13012267383317, -4
+1079, 2.623548302690499, 4.13012267383317, -3
+1080, 2.623548302690499, 4.13012267383317, -2
+1081, 2.623548302690499, 4.13012267383317, -1
+1082, 2.623548302690499, 4.13012267383317, 0
+1083, 2.623548302690499, 4.13012267383317, 1
+1084, 2.623548302690499, 4.13012267383317, 2
+1085, 2.623548302690499, 4.13012267383317, 3
+1086, 2.623548302690499, 4.13012267383317, 4
+1087, -4.12928603258286, -2.623527906833591, -4
+1088, -4.12928603258286, -2.623527906833591, -3
+1089, -4.12928603258286, -2.623527906833591, -2
+1090, -4.12928603258286, -2.623527906833591, -1
+1091, -4.12928603258286, -2.623527906833591, 0
+1092, -4.12928603258286, -2.623527906833591, 1
+1093, -4.12928603258286, -2.623527906833591, 2
+1094, -4.12928603258286, -2.623527906833591, 3
+1095, -4.12928603258286, -2.623527906833591, 4
+1096, -2.624220664337432, 4.143597264629516, -4
+1097, -2.624220664337432, 4.143597264629516, -3
+1098, -2.624220664337432, 4.143597264629516, -2
+1099, -2.624220664337432, 4.143597264629516, -1
+1100, -2.624220664337432, 4.143597264629516, 0
+1101, -2.624220664337432, 4.143597264629516, 1
+1102, -2.624220664337432, 4.143597264629516, 2
+1103, -2.624220664337432, 4.143597264629516, 3
+1104, -2.624220664337432, 4.143597264629516, 4
+1105, 4.089730028010475, 2.776574452224664, -4
+1106, 4.089730028010475, 2.776574452224664, -3
+1107, 4.089730028010475, 2.776574452224664, -2
+1108, 4.089730028010475, 2.776574452224664, -1
+1109, 4.089730028010475, 2.776574452224664, 0
+1110, 4.089730028010475, 2.776574452224664, 1
+1111, 4.089730028010475, 2.776574452224664, 2
+1112, 4.089730028010475, 2.776574452224664, 3
+1113, 4.089730028010475, 2.776574452224664, 4
+1114, 2.629652458540163, -4.129049284126983, -4
+1115, 2.629652458540163, -4.129049284126983, -3
+1116, 2.629652458540163, -4.129049284126983, -2
+1117, 2.629652458540163, -4.129049284126983, -1
+1118, 2.629652458540163, -4.129049284126983, 0
+1119, 2.629652458540163, -4.129049284126983, 1
+1120, 2.629652458540163, -4.129049284126983, 2
+1121, 2.629652458540163, -4.129049284126983, 3
+1122, 2.629652458540163, -4.129049284126983, 4
+1123, 3.302367905051891, 1.70968032242428, -4
+1124, 3.302367905051891, 1.70968032242428, -3
+1125, 3.302367905051891, 1.70968032242428, -2
+1126, 3.302367905051891, 1.70968032242428, -1
+1127, 3.302367905051891, 1.70968032242428, 0
+1128, 3.302367905051891, 1.70968032242428, 1
+1129, 3.302367905051891, 1.70968032242428, 2
+1130, 3.302367905051891, 1.70968032242428, 3
+1131, 3.302367905051891, 1.70968032242428, 4
+1132, 1.53952226362465, -3.315374538869128, -4
+1133, 1.53952226362465, -3.315374538869128, -3
+1134, 1.53952226362465, -3.315374538869128, -2
+1135, 1.53952226362465, -3.315374538869128, -1
+1136, 1.53952226362465, -3.315374538869128, 0
+1137, 1.53952226362465, -3.315374538869128, 1
+1138, 1.53952226362465, -3.315374538869128, 2
+1139, 1.53952226362465, -3.315374538869128, 3
+1140, 1.53952226362465, -3.315374538869128, 4
+1141, -3.332608471710656, -1.535150946932129, -4
+1142, -3.332608471710656, -1.535150946932129, -3
+1143, -3.332608471710656, -1.535150946932129, -2
+1144, -3.332608471710656, -1.535150946932129, -1
+1145, -3.332608471710656, -1.535150946932129, 0
+1146, -3.332608471710656, -1.535150946932129, 1
+1147, -3.332608471710656, -1.535150946932129, 2
+1148, -3.332608471710656, -1.535150946932129, 3
+1149, -3.332608471710656, -1.535150946932129, 4
+1150, -1.526301238015451, 3.329296275968781, -4
+1151, -1.526301238015451, 3.329296275968781, -3
+1152, -1.526301238015451, 3.329296275968781, -2
+1153, -1.526301238015451, 3.329296275968781, -1
+1154, -1.526301238015451, 3.329296275968781, 0
+1155, -1.526301238015451, 3.329296275968781, 1
+1156, -1.526301238015451, 3.329296275968781, 2
+1157, -1.526301238015451, 3.329296275968781, 3
+1158, -1.526301238015451, 3.329296275968781, 4
+1159, -3.320587535849678, 1.683096625180183, -4
+1160, -3.320587535849678, 1.683096625180183, -3
+1161, -3.320587535849678, 1.683096625180183, -2
+1162, -3.320587535849678, 1.683096625180183, -1
+1163, -3.320587535849678, 1.683096625180183, 0
+1164, -3.320587535849678, 1.683096625180183, 1
+1165, -3.320587535849678, 1.683096625180183, 2
+1166, -3.320587535849678, 1.683096625180183, 3
+1167, -3.320587535849678, 1.683096625180183, 4
+1168, -1.683526668391519, -3.324593751159378, -4
+1169, -1.683526668391519, -3.324593751159378, -3
+1170, -1.683526668391519, -3.324593751159378, -2
+1171, -1.683526668391519, -3.324593751159378, -1
+1172, -1.683526668391519, -3.324593751159378, 0
+1173, -1.683526668391519, -3.324593751159378, 1
+1174, -1.683526668391519, -3.324593751159378, 2
+1175, -1.683526668391519, -3.324593751159378, 3
+1176, -1.683526668391519, -3.324593751159378, 4
+1177, 3.327091738802665, -1.68393626645891, -4
+1178, 3.327091738802665, -1.68393626645891, -3
+1179, 3.327091738802665, -1.68393626645891, -2
+1180, 3.327091738802665, -1.68393626645891, -1
+1181, 3.327091738802665, -1.68393626645891, 0
+1182, 3.327091738802665, -1.68393626645891, 1
+1183, 3.327091738802665, -1.68393626645891, 2
+1184, 3.327091738802665, -1.68393626645891, 3
+1185, 3.327091738802665, -1.68393626645891, 4
+1186, 1.639192452215204, 3.325378675743715, -4
+1187, 1.639192452215204, 3.325378675743715, -3
+1188, 1.639192452215204, 3.325378675743715, -2
+1189, 1.639192452215204, 3.325378675743715, -1
+1190, 1.639192452215204, 3.325378675743715, 0
+1191, 1.639192452215204, 3.325378675743715, 1
+1192, 1.639192452215204, 3.325378675743715, 2
+1193, 1.639192452215204, 3.325378675743715, 3
+1194, 1.639192452215204, 3.325378675743715, 4
+1195, 3.287248188077074, -4.189087367530157, -4
+1196, 3.287248188077074, -4.189087367530157, -3
+1197, 3.287248188077074, -4.189087367530157, -2
+1198, 3.287248188077074, -4.189087367530157, -1
+1199, 3.287248188077074, -4.189087367530157, 0
+1200, 3.287248188077074, -4.189087367530157, 1
+1201, 3.287248188077074, -4.189087367530157, 2
+1202, 3.287248188077074, -4.189087367530157, 3
+1203, 3.287248188077074, -4.189087367530157, 4
+1204, -3.295778441163215, 4.201117225082506, -4
+1205, -3.295778441163215, 4.201117225082506, -3
+1206, -3.295778441163215, 4.201117225082506, -2
+1207, -3.295778441163215, 4.201117225082506, -1
+1208, -3.295778441163215, 4.201117225082506, 0
+1209, -3.295778441163215, 4.201117225082506, 1
+1210, -3.295778441163215, 4.201117225082506, 2
+1211, -3.295778441163215, 4.201117225082506, 3
+1212, -3.295778441163215, 4.201117225082506, 4
+1213, -4.178468025548098, -3.275823178071892, -4
+1214, -4.178468025548098, -3.275823178071892, -3
+1215, -4.178468025548098, -3.275823178071892, -2
+1216, -4.178468025548098, -3.275823178071892, -1
+1217, -4.178468025548098, -3.275823178071892, 0
+1218, -4.178468025548098, -3.275823178071892, 1
+1219, -4.178468025548098, -3.275823178071892, 2
+1220, -4.178468025548098, -3.275823178071892, 3
+1221, -4.178468025548098, -3.275823178071892, 4
+1222, 4.12726588759978, 3.663811975302485, -4
+1223, 4.12726588759978, 3.663811975302485, -3
+1224, 4.12726588759978, 3.663811975302485, -2
+1225, 4.12726588759978, 3.663811975302485, -1
+1226, 4.12726588759978, 3.663811975302485, 0
+1227, 4.12726588759978, 3.663811975302485, 1
+1228, 4.12726588759978, 3.663811975302485, 2
+1229, 4.12726588759978, 3.663811975302485, 3
+1230, 4.12726588759978, 3.663811975302485, 4
+1231, -4.106859944586758, 3.331114243505222, -4
+1232, -4.106859944586758, 3.331114243505222, -3
+1233, -4.106859944586758, 3.331114243505222, -2
+1234, -4.106859944586758, 3.331114243505222, -1
+1235, -4.106859944586758, 3.331114243505222, 0
+1236, -4.106859944586758, 3.331114243505222, 1
+1237, -4.106859944586758, 3.331114243505222, 2
+1238, -4.106859944586758, 3.331114243505222, 3
+1239, -4.106859944586758, 3.331114243505222, 4
+1240, 3.285661280600918, 4.188431731448394, -4
+1241, 3.285661280600918, 4.188431731448394, -3
+1242, 3.285661280600918, 4.188431731448394, -2
+1243, 3.285661280600918, 4.188431731448394, -1
+1244, 3.285661280600918, 4.188431731448394, 0
+1245, 3.285661280600918, 4.188431731448394, 1
+1246, 3.285661280600918, 4.188431731448394, 2
+1247, 3.285661280600918, 4.188431731448394, 3
+1248, 3.285661280600918, 4.188431731448394, 4
+1249, -3.286453411102075, -4.188960608239536, -4
+1250, -3.286453411102075, -4.188960608239536, -3
+1251, -3.286453411102075, -4.188960608239536, -2
+1252, -3.286453411102075, -4.188960608239536, -1
+1253, -3.286453411102075, -4.188960608239536, 0
+1254, -3.286453411102075, -4.188960608239536, 1
+1255, -3.286453411102075, -4.188960608239536, 2
+1256, -3.286453411102075, -4.188960608239536, 3
+1257, -3.286453411102075, -4.188960608239536, 4
+1258, 4.191730151615054, -3.289023590308119, -4
+1259, 4.191730151615054, -3.289023590308119, -3
+1260, 4.191730151615054, -3.289023590308119, -2
+1261, 4.191730151615054, -3.289023590308119, -1
+1262, 4.191730151615054, -3.289023590308119, 0
+1263, 4.191730151615054, -3.289023590308119, 1
+1264, 4.191730151615054, -3.289023590308119, 2
+1265, 4.191730151615054, -3.289023590308119, 3
+1266, 4.191730151615054, -3.289023590308119, 4
+1267, -0.8835688592767325, 4.235974096384401, -4
+1268, -0.8835688592767325, 4.235974096384401, -3
+1269, -0.8835688592767325, 4.235974096384401, -2
+1270, -0.8835688592767325, 4.235974096384401, -1
+1271, -0.8835688592767325, 4.235974096384401, 0
+1272, -0.8835688592767325, 4.235974096384401, 1
+1273, -0.8835688592767325, 4.235974096384401, 2
+1274, -0.8835688592767325, 4.235974096384401, 3
+1275, -0.8835688592767325, 4.235974096384401, 4
+1276, 0.9057525013633964, -4.243953044530109, -4
+1277, 0.9057525013633964, -4.243953044530109, -3
+1278, 0.9057525013633964, -4.243953044530109, -2
+1279, 0.9057525013633964, -4.243953044530109, -1
+1280, 0.9057525013633964, -4.243953044530109, 0
+1281, 0.9057525013633964, -4.243953044530109, 1
+1282, 0.9057525013633964, -4.243953044530109, 2
+1283, 0.9057525013633964, -4.243953044530109, 3
+1284, 0.9057525013633964, -4.243953044530109, 4
+1285, -4.241218968046286, -0.9001784922335702, -4
+1286, -4.241218968046286, -0.9001784922335702, -3
+1287, -4.241218968046286, -0.9001784922335702, -2
+1288, -4.241218968046286, -0.9001784922335702, -1
+1289, -4.241218968046286, -0.9001784922335702, 0
+1290, -4.241218968046286, -0.9001784922335702, 1
+1291, -4.241218968046286, -0.9001784922335702, 2
+1292, -4.241218968046286, -0.9001784922335702, 3
+1293, -4.241218968046286, -0.9001784922335702, 4
+1294, 4.228781092544913, 0.9229902907771372, -4
+1295, 4.228781092544913, 0.9229902907771372, -3
+1296, 4.228781092544913, 0.9229902907771372, -2
+1297, 4.228781092544913, 0.9229902907771372, -1
+1298, 4.228781092544913, 0.9229902907771372, 0
+1299, 4.228781092544913, 0.9229902907771372, 1
+1300, 4.228781092544913, 0.9229902907771372, 2
+1301, 4.228781092544913, 0.9229902907771372, 3
+1302, 4.228781092544913, 0.9229902907771372, 4
+1303, -0.9143189641212353, -4.23580907643538, -4
+1304, -0.9143189641212353, -4.23580907643538, -3
+1305, -0.9143189641212353, -4.23580907643538, -2
+1306, -0.9143189641212353, -4.23580907643538, -1
+1307, -0.9143189641212353, -4.23580907643538, 0
+1308, -0.9143189641212353, -4.23580907643538, 1
+1309, -0.9143189641212353, -4.23580907643538, 2
+1310, -0.9143189641212353, -4.23580907643538, 3
+1311, -0.9143189641212353, -4.23580907643538, 4
+1312, 4.236027863443724, -0.9133192803687695, -4
+1313, 4.236027863443724, -0.9133192803687695, -3
+1314, 4.236027863443724, -0.9133192803687695, -2
+1315, 4.236027863443724, -0.9133192803687695, -1
+1316, 4.236027863443724, -0.9133192803687695, 0
+1317, 4.236027863443724, -0.9133192803687695, 1
+1318, 4.236027863443724, -0.9133192803687695, 2
+1319, 4.236027863443724, -0.9133192803687695, 3
+1320, 4.236027863443724, -0.9133192803687695, 4
+1321, 0.9149732711747496, 4.23204845910478, -4
+1322, 0.9149732711747496, 4.23204845910478, -3
+1323, 0.9149732711747496, 4.23204845910478, -2
+1324, 0.9149732711747496, 4.23204845910478, -1
+1325, 0.9149732711747496, 4.23204845910478, 0
+1326, 0.9149732711747496, 4.23204845910478, 1
+1327, 0.9149732711747496, 4.23204845910478, 2
+1328, 0.9149732711747496, 4.23204845910478, 3
+1329, 0.9149732711747496, 4.23204845910478, 4
+1330, -4.232840912800372, 0.9176807031613724, -4
+1331, -4.232840912800372, 0.9176807031613724, -3
+1332, -4.232840912800372, 0.9176807031613724, -2
+1333, -4.232840912800372, 0.9176807031613724, -1
+1334, -4.232840912800372, 0.9176807031613724, 0
+1335, -4.232840912800372, 0.9176807031613724, 1
+1336, -4.232840912800372, 0.9176807031613724, 2
+1337, -4.232840912800372, 0.9176807031613724, 3
+1338, -4.232840912800372, 0.9176807031613724, 4
+1339, 0.008845214798486375, 4.260893647236393, -4
+1340, 0.008845214798486375, 4.260893647236393, -3
+1341, 0.008845214798486375, 4.260893647236393, -2
+1342, 0.008845214798486375, 4.260893647236393, -1
+1343, 0.008845214798486375, 4.260893647236393, 0
+1344, 0.008845214798486375, 4.260893647236393, 1
+1345, 0.008845214798486375, 4.260893647236393, 2
+1346, 0.008845214798486375, 4.260893647236393, 3
+1347, 0.008845214798486375, 4.260893647236393, 4
+1348, 4.267986468814929, 0.001017592833179522, -4
+1349, 4.267986468814929, 0.001017592833179522, -3
+1350, 4.267986468814929, 0.001017592833179522, -2
+1351, 4.267986468814929, 0.001017592833179522, -1
+1352, 4.267986468814929, 0.001017592833179522, 0
+1353, 4.267986468814929, 0.001017592833179522, 1
+1354, 4.267986468814929, 0.001017592833179522, 2
+1355, 4.267986468814929, 0.001017592833179522, 3
+1356, 4.267986468814929, 0.001017592833179522, 4
+1357, -0.002629743415697635, -4.266103757267889, -4
+1358, -0.002629743415697635, -4.266103757267889, -3
+1359, -0.002629743415697635, -4.266103757267889, -2
+1360, -0.002629743415697635, -4.266103757267889, -1
+1361, -0.002629743415697635, -4.266103757267889, 0
+1362, -0.002629743415697635, -4.266103757267889, 1
+1363, -0.002629743415697635, -4.266103757267889, 2
+1364, -0.002629743415697635, -4.266103757267889, 3
+1365, -0.002629743415697635, -4.266103757267889, 4
+1366, -4.262233825920551, 0.005818638193458033, -4
+1367, -4.262233825920551, 0.005818638193458033, -3
+1368, -4.262233825920551, 0.005818638193458033, -2
+1369, -4.262233825920551, 0.005818638193458033, -1
+1370, -4.262233825920551, 0.005818638193458033, 0
+1371, -4.262233825920551, 0.005818638193458033, 1
+1372, -4.262233825920551, 0.005818638193458033, 2
+1373, -4.262233825920551, 0.005818638193458033, 3
+1374, -4.262233825920551, 0.005818638193458033, 4
+1375, 3.592635884483748, -2.634007018244112e-05, -4
+1376, 3.592635884483748, -2.634007018244112e-05, -3
+1377, 3.592635884483748, -2.634007018244112e-05, -2
+1378, 3.592635884483748, -2.634007018244112e-05, -1
+1379, 3.592635884483748, -2.634007018244112e-05, 0
+1380, 3.592635884483748, -2.634007018244112e-05, 1
+1381, 3.592635884483748, -2.634007018244112e-05, 2
+1382, 3.592635884483748, -2.634007018244112e-05, 3
+1383, 3.592635884483748, -2.634007018244112e-05, 4
+1384, -0.000940883892022749, -3.594535563634333, -4
+1385, -0.000940883892022749, -3.594535563634333, -3
+1386, -0.000940883892022749, -3.594535563634333, -2
+1387, -0.000940883892022749, -3.594535563634333, -1
+1388, -0.000940883892022749, -3.594535563634333, 0
+1389, -0.000940883892022749, -3.594535563634333, 1
+1390, -0.000940883892022749, -3.594535563634333, 2
+1391, -0.000940883892022749, -3.594535563634333, 3
+1392, -0.000940883892022749, -3.594535563634333, 4
+1393, -3.577141536751654, 0.005642074623536417, -4
+1394, -3.577141536751654, 0.005642074623536417, -3
+1395, -3.577141536751654, 0.005642074623536417, -2
+1396, -3.577141536751654, 0.005642074623536417, -1
+1397, -3.577141536751654, 0.005642074623536417, 0
+1398, -3.577141536751654, 0.005642074623536417, 1
+1399, -3.577141536751654, 0.005642074623536417, 2
+1400, -3.577141536751654, 0.005642074623536417, 3
+1401, -3.577141536751654, 0.005642074623536417, 4
+1402, 0.00838864987239782, 3.576052457680655, -4
+1403, 0.00838864987239782, 3.576052457680655, -3
+1404, 0.00838864987239782, 3.576052457680655, -2
+1405, 0.00838864987239782, 3.576052457680655, -1
+1406, 0.00838864987239782, 3.576052457680655, 0
+1407, 0.00838864987239782, 3.576052457680655, 1
+1408, 0.00838864987239782, 3.576052457680655, 2
+1409, 0.00838864987239782, 3.576052457680655, 3
+1410, 0.00838864987239782, 3.576052457680655, 4
+*Element, type=T3D2, ELSET=Line1
+19, 1, 19
+20, 19, 20
+21, 20, 21
+22, 21, 22
+23, 22, 23
+24, 23, 24
+25, 24, 25
+26, 25, 26
+27, 26, 27
+28, 27, 2
+*Element, type=T3D2, ELSET=Line2
+29, 2, 28
+30, 28, 29
+31, 29, 30
+32, 30, 31
+33, 31, 32
+34, 32, 33
+35, 33, 34
+36, 34, 35
+37, 35, 36
+38, 36, 3
+*Element, type=T3D2, ELSET=Line3
+39, 3, 37
+40, 37, 38
+41, 38, 39
+42, 39, 40
+43, 40, 41
+44, 41, 42
+45, 42, 43
+46, 43, 44
+47, 44, 45
+48, 45, 4
+*Element, type=T3D2, ELSET=Line4
+49, 4, 46
+50, 46, 47
+51, 47, 48
+52, 48, 49
+53, 49, 50
+54, 50, 51
+55, 51, 52
+56, 52, 53
+57, 53, 54
+58, 54, 1
+*Element, type=T3D2, ELSET=Line5
+59, 8, 55
+60, 55, 56
+61, 56, 57
+62, 57, 58
+63, 58, 59
+64, 59, 7
+*Element, type=T3D2, ELSET=Line6
+65, 7, 60
+66, 60, 61
+67, 61, 62
+68, 62, 63
+69, 63, 64
+70, 64, 9
+*Element, type=T3D2, ELSET=Line7
+71, 9, 65
+72, 65, 66
+73, 66, 67
+74, 67, 68
+75, 68, 69
+76, 69, 6
+*Element, type=T3D2, ELSET=Line8
+77, 6, 70
+78, 70, 71
+79, 71, 72
+80, 72, 73
+81, 73, 74
+82, 74, 8
+*Element, type=T3D2, ELSET=Line13
+83, 10, 75
+84, 75, 76
+85, 76, 77
+86, 77, 78
+87, 78, 79
+88, 79, 80
+89, 80, 81
+90, 81, 82
+91, 82, 83
+92, 83, 11
+*Element, type=T3D2, ELSET=Line14
+93, 11, 84
+94, 84, 85
+95, 85, 86
+96, 86, 87
+97, 87, 88
+98, 88, 89
+99, 89, 90
+100, 90, 91
+101, 91, 92
+102, 92, 12
+*Element, type=T3D2, ELSET=Line15
+103, 12, 93
+104, 93, 94
+105, 94, 95
+106, 95, 96
+107, 96, 97
+108, 97, 98
+109, 98, 99
+110, 99, 100
+111, 100, 101
+112, 101, 13
+*Element, type=T3D2, ELSET=Line16
+113, 13, 102
+114, 102, 103
+115, 103, 104
+116, 104, 105
+117, 105, 106
+118, 106, 107
+119, 107, 108
+120, 108, 109
+121, 109, 110
+122, 110, 10
+*Element, type=T3D2, ELSET=Line17
+123, 14, 111
+124, 111, 112
+125, 112, 113
+126, 113, 114
+127, 114, 115
+128, 115, 16
+*Element, type=T3D2, ELSET=Line18
+129, 16, 116
+130, 116, 117
+131, 117, 118
+132, 118, 119
+133, 119, 120
+134, 120, 17
+*Element, type=T3D2, ELSET=Line19
+135, 17, 121
+136, 121, 122
+137, 122, 123
+138, 123, 124
+139, 124, 125
+140, 125, 18
+*Element, type=T3D2, ELSET=Line20
+141, 18, 126
+142, 126, 127
+143, 127, 128
+144, 128, 129
+145, 129, 130
+146, 130, 14
+*Element, type=T3D2, ELSET=Line22
+147, 1, 131
+148, 131, 132
+149, 132, 133
+150, 133, 134
+151, 134, 135
+152, 135, 136
+153, 136, 137
+154, 137, 138
+155, 138, 139
+156, 139, 10
+*Element, type=T3D2, ELSET=Line23
+157, 2, 140
+158, 140, 141
+159, 141, 142
+160, 142, 143
+161, 143, 144
+162, 144, 145
+163, 145, 146
+164, 146, 147
+165, 147, 148
+166, 148, 11
+*Element, type=T3D2, ELSET=Line27
+167, 3, 149
+168, 149, 150
+169, 150, 151
+170, 151, 152
+171, 152, 153
+172, 153, 154
+173, 154, 155
+174, 155, 156
+175, 156, 157
+176, 157, 12
+*Element, type=T3D2, ELSET=Line31
+177, 4, 158
+178, 158, 159
+179, 159, 160
+180, 160, 161
+181, 161, 162
+182, 162, 163
+183, 163, 164
+184, 164, 165
+185, 165, 166
+186, 166, 13
+*Element, type=T3D2, ELSET=Line38
+187, 8, 167
+188, 167, 168
+189, 168, 169
+190, 169, 170
+191, 170, 171
+192, 171, 172
+193, 172, 173
+194, 173, 174
+195, 174, 175
+196, 175, 14
+*Element, type=T3D2, ELSET=Line39
+197, 6, 176
+198, 176, 177
+199, 177, 178
+200, 178, 179
+201, 179, 180
+202, 180, 181
+203, 181, 182
+204, 182, 183
+205, 183, 184
+206, 184, 16
+*Element, type=T3D2, ELSET=Line43
+207, 9, 185
+208, 185, 186
+209, 186, 187
+210, 187, 188
+211, 188, 189
+212, 189, 190
+213, 190, 191
+214, 191, 192
+215, 192, 193
+216, 193, 17
+*Element, type=T3D2, ELSET=Line47
+217, 7, 194
+218, 194, 195
+219, 195, 196
+220, 196, 197
+221, 197, 198
+222, 198, 199
+223, 199, 200
+224, 200, 201
+225, 201, 202
+226, 202, 18
+*Element, type=CPS4, ELSET=Surface11
+1203, 28, 243, 27, 2
+1204, 37, 246, 36, 3
+1205, 46, 244, 45, 4
+1206, 19, 245, 54, 1
+1211, 62, 61, 238, 224
+1212, 57, 56, 235, 223
+1213, 72, 71, 236, 226
+1214, 67, 66, 237, 225
+1215, 67, 225, 240, 68
+1216, 57, 223, 242, 58
+1217, 62, 224, 239, 63
+1218, 72, 226, 241, 73
+1219, 6, 264, 219, 70
+1220, 8, 263, 221, 55
+1221, 7, 266, 220, 60
+1222, 9, 265, 222, 65
+1223, 228, 208, 240, 225
+1224, 230, 210, 242, 223
+1225, 227, 207, 239, 224
+1226, 229, 209, 241, 226
+1227, 231, 225, 237, 212
+1228, 233, 223, 235, 214
+1229, 232, 224, 238, 213
+1230, 234, 226, 236, 211
+1231, 217, 265, 9, 64
+1232, 218, 266, 7, 59
+1233, 216, 263, 8, 74
+1234, 215, 264, 6, 69
+1235, 258, 49, 50, 262
+1236, 257, 40, 41, 259
+1237, 256, 31, 32, 260
+1238, 255, 22, 23, 261
+1239, 208, 228, 20, 21
+1240, 207, 227, 47, 48
+1241, 209, 229, 29, 30
+1242, 210, 230, 38, 39
+1243, 214, 34, 35, 233
+1244, 212, 52, 53, 231
+1245, 213, 43, 44, 232
+1246, 211, 25, 26, 234
+1247, 241, 209, 256, 216
+1248, 239, 207, 258, 217
+1249, 242, 210, 257, 218
+1250, 240, 208, 255, 215
+1251, 252, 261, 23, 24
+1252, 254, 260, 32, 33
+1253, 253, 262, 50, 51
+1254, 251, 259, 41, 42
+1255, 237, 222, 253, 212
+1256, 236, 219, 252, 211
+1257, 235, 221, 254, 214
+1258, 238, 220, 251, 213
+1259, 212, 253, 51, 52
+1260, 213, 251, 42, 43
+1261, 211, 252, 24, 25
+1262, 214, 254, 33, 34
+1263, 226, 234, 243, 204
+1264, 224, 232, 244, 205
+1265, 225, 231, 245, 203
+1266, 223, 233, 246, 206
+1267, 223, 206, 248, 230
+1268, 226, 204, 250, 229
+1269, 224, 205, 247, 227
+1270, 225, 203, 249, 228
+1271, 209, 30, 31, 256
+1272, 208, 21, 22, 255
+1273, 210, 39, 40, 257
+1274, 207, 48, 49, 258
+1279, 205, 244, 46, 247
+1280, 203, 245, 19, 249
+1281, 204, 243, 28, 250
+1282, 206, 246, 37, 248
+1283, 46, 47, 227, 247
+1284, 19, 20, 228, 249
+1285, 28, 29, 229, 250
+1286, 37, 38, 230, 248
+1287, 27, 243, 234, 26
+1288, 45, 244, 232, 44
+1289, 54, 245, 231, 53
+1290, 36, 246, 233, 35
+1291, 217, 64, 63, 239
+1292, 215, 69, 68, 240
+1293, 216, 74, 73, 241
+1294, 218, 59, 58, 242
+1295, 220, 238, 61, 60
+1296, 221, 235, 56, 55
+1297, 222, 237, 66, 65
+1298, 219, 236, 71, 70
+1299, 257, 259, 266, 218
+1300, 255, 261, 264, 215
+1301, 258, 262, 265, 217
+1302, 256, 260, 263, 216
+1303, 252, 219, 264, 261
+1304, 251, 220, 266, 259
+1305, 254, 221, 263, 260
+1306, 253, 222, 265, 262
+*Element, type=CPS4, ELSET=Surface24
+1307, 1, 19, 267, 131
+1308, 131, 267, 268, 132
+1309, 132, 268, 269, 133
+1310, 133, 269, 270, 134
+1311, 134, 270, 271, 135
+1312, 135, 271, 272, 136
+1313, 136, 272, 273, 137
+1314, 137, 273, 274, 138
+1315, 138, 274, 275, 139
+1316, 139, 275, 75, 10
+1317, 19, 20, 276, 267
+1318, 267, 276, 277, 268
+1319, 268, 277, 278, 269
+1320, 269, 278, 279, 270
+1321, 270, 279, 280, 271
+1322, 271, 280, 281, 272
+1323, 272, 281, 282, 273
+1324, 273, 282, 283, 274
+1325, 274, 283, 284, 275
+1326, 275, 284, 76, 75
+1327, 20, 21, 285, 276
+1328, 276, 285, 286, 277
+1329, 277, 286, 287, 278
+1330, 278, 287, 288, 279
+1331, 279, 288, 289, 280
+1332, 280, 289, 290, 281
+1333, 281, 290, 291, 282
+1334, 282, 291, 292, 283
+1335, 283, 292, 293, 284
+1336, 284, 293, 77, 76
+1337, 21, 22, 294, 285
+1338, 285, 294, 295, 286
+1339, 286, 295, 296, 287
+1340, 287, 296, 297, 288
+1341, 288, 297, 298, 289
+1342, 289, 298, 299, 290
+1343, 290, 299, 300, 291
+1344, 291, 300, 301, 292
+1345, 292, 301, 302, 293
+1346, 293, 302, 78, 77
+1347, 22, 23, 303, 294
+1348, 294, 303, 304, 295
+1349, 295, 304, 305, 296
+1350, 296, 305, 306, 297
+1351, 297, 306, 307, 298
+1352, 298, 307, 308, 299
+1353, 299, 308, 309, 300
+1354, 300, 309, 310, 301
+1355, 301, 310, 311, 302
+1356, 302, 311, 79, 78
+1357, 23, 24, 312, 303
+1358, 303, 312, 313, 304
+1359, 304, 313, 314, 305
+1360, 305, 314, 315, 306
+1361, 306, 315, 316, 307
+1362, 307, 316, 317, 308
+1363, 308, 317, 318, 309
+1364, 309, 318, 319, 310
+1365, 310, 319, 320, 311
+1366, 311, 320, 80, 79
+1367, 24, 25, 321, 312
+1368, 312, 321, 322, 313
+1369, 313, 322, 323, 314
+1370, 314, 323, 324, 315
+1371, 315, 324, 325, 316
+1372, 316, 325, 326, 317
+1373, 317, 326, 327, 318
+1374, 318, 327, 328, 319
+1375, 319, 328, 329, 320
+1376, 320, 329, 81, 80
+1377, 25, 26, 330, 321
+1378, 321, 330, 331, 322
+1379, 322, 331, 332, 323
+1380, 323, 332, 333, 324
+1381, 324, 333, 334, 325
+1382, 325, 334, 335, 326
+1383, 326, 335, 336, 327
+1384, 327, 336, 337, 328
+1385, 328, 337, 338, 329
+1386, 329, 338, 82, 81
+1387, 26, 27, 339, 330
+1388, 330, 339, 340, 331
+1389, 331, 340, 341, 332
+1390, 332, 341, 342, 333
+1391, 333, 342, 343, 334
+1392, 334, 343, 344, 335
+1393, 335, 344, 345, 336
+1394, 336, 345, 346, 337
+1395, 337, 346, 347, 338
+1396, 338, 347, 83, 82
+1397, 27, 2, 140, 339
+1398, 339, 140, 141, 340
+1399, 340, 141, 142, 341
+1400, 341, 142, 143, 342
+1401, 342, 143, 144, 343
+1402, 343, 144, 145, 344
+1403, 344, 145, 146, 345
+1404, 345, 146, 147, 346
+1405, 346, 147, 148, 347
+1406, 347, 148, 11, 83
+*Element, type=CPS4, ELSET=Surface28
+1407, 2, 28, 348, 140
+1408, 140, 348, 349, 141
+1409, 141, 349, 350, 142
+1410, 142, 350, 351, 143
+1411, 143, 351, 352, 144
+1412, 144, 352, 353, 145
+1413, 145, 353, 354, 146
+1414, 146, 354, 355, 147
+1415, 147, 355, 356, 148
+1416, 148, 356, 84, 11
+1417, 28, 29, 357, 348
+1418, 348, 357, 358, 349
+1419, 349, 358, 359, 350
+1420, 350, 359, 360, 351
+1421, 351, 360, 361, 352
+1422, 352, 361, 362, 353
+1423, 353, 362, 363, 354
+1424, 354, 363, 364, 355
+1425, 355, 364, 365, 356
+1426, 356, 365, 85, 84
+1427, 29, 30, 366, 357
+1428, 357, 366, 367, 358
+1429, 358, 367, 368, 359
+1430, 359, 368, 369, 360
+1431, 360, 369, 370, 361
+1432, 361, 370, 371, 362
+1433, 362, 371, 372, 363
+1434, 363, 372, 373, 364
+1435, 364, 373, 374, 365
+1436, 365, 374, 86, 85
+1437, 30, 31, 375, 366
+1438, 366, 375, 376, 367
+1439, 367, 376, 377, 368
+1440, 368, 377, 378, 369
+1441, 369, 378, 379, 370
+1442, 370, 379, 380, 371
+1443, 371, 380, 381, 372
+1444, 372, 381, 382, 373
+1445, 373, 382, 383, 374
+1446, 374, 383, 87, 86
+1447, 31, 32, 384, 375
+1448, 375, 384, 385, 376
+1449, 376, 385, 386, 377
+1450, 377, 386, 387, 378
+1451, 378, 387, 388, 379
+1452, 379, 388, 389, 380
+1453, 380, 389, 390, 381
+1454, 381, 390, 391, 382
+1455, 382, 391, 392, 383
+1456, 383, 392, 88, 87
+1457, 32, 33, 393, 384
+1458, 384, 393, 394, 385
+1459, 385, 394, 395, 386
+1460, 386, 395, 396, 387
+1461, 387, 396, 397, 388
+1462, 388, 397, 398, 389
+1463, 389, 398, 399, 390
+1464, 390, 399, 400, 391
+1465, 391, 400, 401, 392
+1466, 392, 401, 89, 88
+1467, 33, 34, 402, 393
+1468, 393, 402, 403, 394
+1469, 394, 403, 404, 395
+1470, 395, 404, 405, 396
+1471, 396, 405, 406, 397
+1472, 397, 406, 407, 398
+1473, 398, 407, 408, 399
+1474, 399, 408, 409, 400
+1475, 400, 409, 410, 401
+1476, 401, 410, 90, 89
+1477, 34, 35, 411, 402
+1478, 402, 411, 412, 403
+1479, 403, 412, 413, 404
+1480, 404, 413, 414, 405
+1481, 405, 414, 415, 406
+1482, 406, 415, 416, 407
+1483, 407, 416, 417, 408
+1484, 408, 417, 418, 409
+1485, 409, 418, 419, 410
+1486, 410, 419, 91, 90
+1487, 35, 36, 420, 411
+1488, 411, 420, 421, 412
+1489, 412, 421, 422, 413
+1490, 413, 422, 423, 414
+1491, 414, 423, 424, 415
+1492, 415, 424, 425, 416
+1493, 416, 425, 426, 417
+1494, 417, 426, 427, 418
+1495, 418, 427, 428, 419
+1496, 419, 428, 92, 91
+1497, 36, 3, 149, 420
+1498, 420, 149, 150, 421
+1499, 421, 150, 151, 422
+1500, 422, 151, 152, 423
+1501, 423, 152, 153, 424
+1502, 424, 153, 154, 425
+1503, 425, 154, 155, 426
+1504, 426, 155, 156, 427
+1505, 427, 156, 157, 428
+1506, 428, 157, 12, 92
+*Element, type=CPS4, ELSET=Surface32
+1507, 3, 37, 429, 149
+1508, 149, 429, 430, 150
+1509, 150, 430, 431, 151
+1510, 151, 431, 432, 152
+1511, 152, 432, 433, 153
+1512, 153, 433, 434, 154
+1513, 154, 434, 435, 155
+1514, 155, 435, 436, 156
+1515, 156, 436, 437, 157
+1516, 157, 437, 93, 12
+1517, 37, 38, 438, 429
+1518, 429, 438, 439, 430
+1519, 430, 439, 440, 431
+1520, 431, 440, 441, 432
+1521, 432, 441, 442, 433
+1522, 433, 442, 443, 434
+1523, 434, 443, 444, 435
+1524, 435, 444, 445, 436
+1525, 436, 445, 446, 437
+1526, 437, 446, 94, 93
+1527, 38, 39, 447, 438
+1528, 438, 447, 448, 439
+1529, 439, 448, 449, 440
+1530, 440, 449, 450, 441
+1531, 441, 450, 451, 442
+1532, 442, 451, 452, 443
+1533, 443, 452, 453, 444
+1534, 444, 453, 454, 445
+1535, 445, 454, 455, 446
+1536, 446, 455, 95, 94
+1537, 39, 40, 456, 447
+1538, 447, 456, 457, 448
+1539, 448, 457, 458, 449
+1540, 449, 458, 459, 450
+1541, 450, 459, 460, 451
+1542, 451, 460, 461, 452
+1543, 452, 461, 462, 453
+1544, 453, 462, 463, 454
+1545, 454, 463, 464, 455
+1546, 455, 464, 96, 95
+1547, 40, 41, 465, 456
+1548, 456, 465, 466, 457
+1549, 457, 466, 467, 458
+1550, 458, 467, 468, 459
+1551, 459, 468, 469, 460
+1552, 460, 469, 470, 461
+1553, 461, 470, 471, 462
+1554, 462, 471, 472, 463
+1555, 463, 472, 473, 464
+1556, 464, 473, 97, 96
+1557, 41, 42, 474, 465
+1558, 465, 474, 475, 466
+1559, 466, 475, 476, 467
+1560, 467, 476, 477, 468
+1561, 468, 477, 478, 469
+1562, 469, 478, 479, 470
+1563, 470, 479, 480, 471
+1564, 471, 480, 481, 472
+1565, 472, 481, 482, 473
+1566, 473, 482, 98, 97
+1567, 42, 43, 483, 474
+1568, 474, 483, 484, 475
+1569, 475, 484, 485, 476
+1570, 476, 485, 486, 477
+1571, 477, 486, 487, 478
+1572, 478, 487, 488, 479
+1573, 479, 488, 489, 480
+1574, 480, 489, 490, 481
+1575, 481, 490, 491, 482
+1576, 482, 491, 99, 98
+1577, 43, 44, 492, 483
+1578, 483, 492, 493, 484
+1579, 484, 493, 494, 485
+1580, 485, 494, 495, 486
+1581, 486, 495, 496, 487
+1582, 487, 496, 497, 488
+1583, 488, 497, 498, 489
+1584, 489, 498, 499, 490
+1585, 490, 499, 500, 491
+1586, 491, 500, 100, 99
+1587, 44, 45, 501, 492
+1588, 492, 501, 502, 493
+1589, 493, 502, 503, 494
+1590, 494, 503, 504, 495
+1591, 495, 504, 505, 496
+1592, 496, 505, 506, 497
+1593, 497, 506, 507, 498
+1594, 498, 507, 508, 499
+1595, 499, 508, 509, 500
+1596, 500, 509, 101, 100
+1597, 45, 4, 158, 501
+1598, 501, 158, 159, 502
+1599, 502, 159, 160, 503
+1600, 503, 160, 161, 504
+1601, 504, 161, 162, 505
+1602, 505, 162, 163, 506
+1603, 506, 163, 164, 507
+1604, 507, 164, 165, 508
+1605, 508, 165, 166, 509
+1606, 509, 166, 13, 101
+*Element, type=CPS4, ELSET=Surface36
+1607, 4, 46, 510, 158
+1608, 158, 510, 511, 159
+1609, 159, 511, 512, 160
+1610, 160, 512, 513, 161
+1611, 161, 513, 514, 162
+1612, 162, 514, 515, 163
+1613, 163, 515, 516, 164
+1614, 164, 516, 517, 165
+1615, 165, 517, 518, 166
+1616, 166, 518, 102, 13
+1617, 46, 47, 519, 510
+1618, 510, 519, 520, 511
+1619, 511, 520, 521, 512
+1620, 512, 521, 522, 513
+1621, 513, 522, 523, 514
+1622, 514, 523, 524, 515
+1623, 515, 524, 525, 516
+1624, 516, 525, 526, 517
+1625, 517, 526, 527, 518
+1626, 518, 527, 103, 102
+1627, 47, 48, 528, 519
+1628, 519, 528, 529, 520
+1629, 520, 529, 530, 521
+1630, 521, 530, 531, 522
+1631, 522, 531, 532, 523
+1632, 523, 532, 533, 524
+1633, 524, 533, 534, 525
+1634, 525, 534, 535, 526
+1635, 526, 535, 536, 527
+1636, 527, 536, 104, 103
+1637, 48, 49, 537, 528
+1638, 528, 537, 538, 529
+1639, 529, 538, 539, 530
+1640, 530, 539, 540, 531
+1641, 531, 540, 541, 532
+1642, 532, 541, 542, 533
+1643, 533, 542, 543, 534
+1644, 534, 543, 544, 535
+1645, 535, 544, 545, 536
+1646, 536, 545, 105, 104
+1647, 49, 50, 546, 537
+1648, 537, 546, 547, 538
+1649, 538, 547, 548, 539
+1650, 539, 548, 549, 540
+1651, 540, 549, 550, 541
+1652, 541, 550, 551, 542
+1653, 542, 551, 552, 543
+1654, 543, 552, 553, 544
+1655, 544, 553, 554, 545
+1656, 545, 554, 106, 105
+1657, 50, 51, 555, 546
+1658, 546, 555, 556, 547
+1659, 547, 556, 557, 548
+1660, 548, 557, 558, 549
+1661, 549, 558, 559, 550
+1662, 550, 559, 560, 551
+1663, 551, 560, 561, 552
+1664, 552, 561, 562, 553
+1665, 553, 562, 563, 554
+1666, 554, 563, 107, 106
+1667, 51, 52, 564, 555
+1668, 555, 564, 565, 556
+1669, 556, 565, 566, 557
+1670, 557, 566, 567, 558
+1671, 558, 567, 568, 559
+1672, 559, 568, 569, 560
+1673, 560, 569, 570, 561
+1674, 561, 570, 571, 562
+1675, 562, 571, 572, 563
+1676, 563, 572, 108, 107
+1677, 52, 53, 573, 564
+1678, 564, 573, 574, 565
+1679, 565, 574, 575, 566
+1680, 566, 575, 576, 567
+1681, 567, 576, 577, 568
+1682, 568, 577, 578, 569
+1683, 569, 578, 579, 570
+1684, 570, 579, 580, 571
+1685, 571, 580, 581, 572
+1686, 572, 581, 109, 108
+1687, 53, 54, 582, 573
+1688, 573, 582, 583, 574
+1689, 574, 583, 584, 575
+1690, 575, 584, 585, 576
+1691, 576, 585, 586, 577
+1692, 577, 586, 587, 578
+1693, 578, 587, 588, 579
+1694, 579, 588, 589, 580
+1695, 580, 589, 590, 581
+1696, 581, 590, 110, 109
+1697, 54, 1, 131, 582
+1698, 582, 131, 132, 583
+1699, 583, 132, 133, 584
+1700, 584, 133, 134, 585
+1701, 585, 134, 135, 586
+1702, 586, 135, 136, 587
+1703, 587, 136, 137, 588
+1704, 588, 137, 138, 589
+1705, 589, 138, 139, 590
+1706, 590, 139, 10, 110
+*Element, type=CPS4, ELSET=Surface40
+1707, 6, 176, 591, 70
+1708, 176, 177, 592, 591
+1709, 177, 178, 593, 592
+1710, 178, 179, 594, 593
+1711, 179, 180, 595, 594
+1712, 180, 181, 596, 595
+1713, 181, 182, 597, 596
+1714, 182, 183, 598, 597
+1715, 183, 184, 599, 598
+1716, 184, 16, 115, 599
+1717, 70, 591, 600, 71
+1718, 591, 592, 601, 600
+1719, 592, 593, 602, 601
+1720, 593, 594, 603, 602
+1721, 594, 595, 604, 603
+1722, 595, 596, 605, 604
+1723, 596, 597, 606, 605
+1724, 597, 598, 607, 606
+1725, 598, 599, 608, 607
+1726, 599, 115, 114, 608
+1727, 71, 600, 609, 72
+1728, 600, 601, 610, 609
+1729, 601, 602, 611, 610
+1730, 602, 603, 612, 611
+1731, 603, 604, 613, 612
+1732, 604, 605, 614, 613
+1733, 605, 606, 615, 614
+1734, 606, 607, 616, 615
+1735, 607, 608, 617, 616
+1736, 608, 114, 113, 617
+1737, 72, 609, 618, 73
+1738, 609, 610, 619, 618
+1739, 610, 611, 620, 619
+1740, 611, 612, 621, 620
+1741, 612, 613, 622, 621
+1742, 613, 614, 623, 622
+1743, 614, 615, 624, 623
+1744, 615, 616, 625, 624
+1745, 616, 617, 626, 625
+1746, 617, 113, 112, 626
+1747, 73, 618, 627, 74
+1748, 618, 619, 628, 627
+1749, 619, 620, 629, 628
+1750, 620, 621, 630, 629
+1751, 621, 622, 631, 630
+1752, 622, 623, 632, 631
+1753, 623, 624, 633, 632
+1754, 624, 625, 634, 633
+1755, 625, 626, 635, 634
+1756, 626, 112, 111, 635
+1757, 74, 627, 167, 8
+1758, 627, 628, 168, 167
+1759, 628, 629, 169, 168
+1760, 629, 630, 170, 169
+1761, 630, 631, 171, 170
+1762, 631, 632, 172, 171
+1763, 632, 633, 173, 172
+1764, 633, 634, 174, 173
+1765, 634, 635, 175, 174
+1766, 635, 111, 14, 175
+*Element, type=CPS4, ELSET=Surface44
+1767, 9, 185, 636, 65
+1768, 185, 186, 637, 636
+1769, 186, 187, 638, 637
+1770, 187, 188, 639, 638
+1771, 188, 189, 640, 639
+1772, 189, 190, 641, 640
+1773, 190, 191, 642, 641
+1774, 191, 192, 643, 642
+1775, 192, 193, 644, 643
+1776, 193, 17, 120, 644
+1777, 65, 636, 645, 66
+1778, 636, 637, 646, 645
+1779, 637, 638, 647, 646
+1780, 638, 639, 648, 647
+1781, 639, 640, 649, 648
+1782, 640, 641, 650, 649
+1783, 641, 642, 651, 650
+1784, 642, 643, 652, 651
+1785, 643, 644, 653, 652
+1786, 644, 120, 119, 653
+1787, 66, 645, 654, 67
+1788, 645, 646, 655, 654
+1789, 646, 647, 656, 655
+1790, 647, 648, 657, 656
+1791, 648, 649, 658, 657
+1792, 649, 650, 659, 658
+1793, 650, 651, 660, 659
+1794, 651, 652, 661, 660
+1795, 652, 653, 662, 661
+1796, 653, 119, 118, 662
+1797, 67, 654, 663, 68
+1798, 654, 655, 664, 663
+1799, 655, 656, 665, 664
+1800, 656, 657, 666, 665
+1801, 657, 658, 667, 666
+1802, 658, 659, 668, 667
+1803, 659, 660, 669, 668
+1804, 660, 661, 670, 669
+1805, 661, 662, 671, 670
+1806, 662, 118, 117, 671
+1807, 68, 663, 672, 69
+1808, 663, 664, 673, 672
+1809, 664, 665, 674, 673
+1810, 665, 666, 675, 674
+1811, 666, 667, 676, 675
+1812, 667, 668, 677, 676
+1813, 668, 669, 678, 677
+1814, 669, 670, 679, 678
+1815, 670, 671, 680, 679
+1816, 671, 117, 116, 680
+1817, 69, 672, 176, 6
+1818, 672, 673, 177, 176
+1819, 673, 674, 178, 177
+1820, 674, 675, 179, 178
+1821, 675, 676, 180, 179
+1822, 676, 677, 181, 180
+1823, 677, 678, 182, 181
+1824, 678, 679, 183, 182
+1825, 679, 680, 184, 183
+1826, 680, 116, 16, 184
+*Element, type=CPS4, ELSET=Surface48
+1827, 7, 194, 681, 60
+1828, 194, 195, 682, 681
+1829, 195, 196, 683, 682
+1830, 196, 197, 684, 683
+1831, 197, 198, 685, 684
+1832, 198, 199, 686, 685
+1833, 199, 200, 687, 686
+1834, 200, 201, 688, 687
+1835, 201, 202, 689, 688
+1836, 202, 18, 125, 689
+1837, 60, 681, 690, 61
+1838, 681, 682, 691, 690
+1839, 682, 683, 692, 691
+1840, 683, 684, 693, 692
+1841, 684, 685, 694, 693
+1842, 685, 686, 695, 694
+1843, 686, 687, 696, 695
+1844, 687, 688, 697, 696
+1845, 688, 689, 698, 697
+1846, 689, 125, 124, 698
+1847, 61, 690, 699, 62
+1848, 690, 691, 700, 699
+1849, 691, 692, 701, 700
+1850, 692, 693, 702, 701
+1851, 693, 694, 703, 702
+1852, 694, 695, 704, 703
+1853, 695, 696, 705, 704
+1854, 696, 697, 706, 705
+1855, 697, 698, 707, 706
+1856, 698, 124, 123, 707
+1857, 62, 699, 708, 63
+1858, 699, 700, 709, 708
+1859, 700, 701, 710, 709
+1860, 701, 702, 711, 710
+1861, 702, 703, 712, 711
+1862, 703, 704, 713, 712
+1863, 704, 705, 714, 713
+1864, 705, 706, 715, 714
+1865, 706, 707, 716, 715
+1866, 707, 123, 122, 716
+1867, 63, 708, 717, 64
+1868, 708, 709, 718, 717
+1869, 709, 710, 719, 718
+1870, 710, 711, 720, 719
+1871, 711, 712, 721, 720
+1872, 712, 713, 722, 721
+1873, 713, 714, 723, 722
+1874, 714, 715, 724, 723
+1875, 715, 716, 725, 724
+1876, 716, 122, 121, 725
+1877, 64, 717, 185, 9
+1878, 717, 718, 186, 185
+1879, 718, 719, 187, 186
+1880, 719, 720, 188, 187
+1881, 720, 721, 189, 188
+1882, 721, 722, 190, 189
+1883, 722, 723, 191, 190
+1884, 723, 724, 192, 191
+1885, 724, 725, 193, 192
+1886, 725, 121, 17, 193
+*Element, type=CPS4, ELSET=Surface52
+1887, 8, 167, 726, 55
+1888, 167, 168, 727, 726
+1889, 168, 169, 728, 727
+1890, 169, 170, 729, 728
+1891, 170, 171, 730, 729
+1892, 171, 172, 731, 730
+1893, 172, 173, 732, 731
+1894, 173, 174, 733, 732
+1895, 174, 175, 734, 733
+1896, 175, 14, 130, 734
+1897, 55, 726, 735, 56
+1898, 726, 727, 736, 735
+1899, 727, 728, 737, 736
+1900, 728, 729, 738, 737
+1901, 729, 730, 739, 738
+1902, 730, 731, 740, 739
+1903, 731, 732, 741, 740
+1904, 732, 733, 742, 741
+1905, 733, 734, 743, 742
+1906, 734, 130, 129, 743
+1907, 56, 735, 744, 57
+1908, 735, 736, 745, 744
+1909, 736, 737, 746, 745
+1910, 737, 738, 747, 746
+1911, 738, 739, 748, 747
+1912, 739, 740, 749, 748
+1913, 740, 741, 750, 749
+1914, 741, 742, 751, 750
+1915, 742, 743, 752, 751
+1916, 743, 129, 128, 752
+1917, 57, 744, 753, 58
+1918, 744, 745, 754, 753
+1919, 745, 746, 755, 754
+1920, 746, 747, 756, 755
+1921, 747, 748, 757, 756
+1922, 748, 749, 758, 757
+1923, 749, 750, 759, 758
+1924, 750, 751, 760, 759
+1925, 751, 752, 761, 760
+1926, 752, 128, 127, 761
+1927, 58, 753, 762, 59
+1928, 753, 754, 763, 762
+1929, 754, 755, 764, 763
+1930, 755, 756, 765, 764
+1931, 756, 757, 766, 765
+1932, 757, 758, 767, 766
+1933, 758, 759, 768, 767
+1934, 759, 760, 769, 768
+1935, 760, 761, 770, 769
+1936, 761, 127, 126, 770
+1937, 59, 762, 194, 7
+1938, 762, 763, 195, 194
+1939, 763, 764, 196, 195
+1940, 764, 765, 197, 196
+1941, 765, 766, 198, 197
+1942, 766, 767, 199, 198
+1943, 767, 768, 200, 199
+1944, 768, 769, 201, 200
+1945, 769, 770, 202, 201
+1946, 770, 126, 18, 202
+*Element, type=CPS4, ELSET=Surface53
+1947, 84, 811, 83, 11
+1948, 93, 814, 92, 12
+1949, 102, 812, 101, 13
+1950, 75, 813, 110, 10
+1951, 123, 124, 806, 792
+1952, 128, 129, 803, 791
+1953, 113, 114, 804, 794
+1954, 118, 119, 805, 793
+1955, 118, 793, 808, 117
+1956, 128, 791, 810, 127
+1957, 123, 792, 807, 122
+1958, 113, 794, 809, 112
+1959, 16, 832, 787, 115
+1960, 14, 831, 789, 130
+1961, 18, 834, 788, 125
+1962, 17, 833, 790, 120
+1963, 796, 776, 808, 793
+1964, 798, 778, 810, 791
+1965, 795, 775, 807, 792
+1966, 797, 777, 809, 794
+1967, 799, 793, 805, 780
+1968, 801, 791, 803, 782
+1969, 800, 792, 806, 781
+1970, 802, 794, 804, 779
+1971, 785, 833, 17, 121
+1972, 786, 834, 18, 126
+1973, 784, 831, 14, 111
+1974, 783, 832, 16, 116
+1975, 826, 105, 106, 830
+1976, 825, 96, 97, 827
+1977, 824, 87, 88, 828
+1978, 823, 78, 79, 829
+1979, 776, 796, 76, 77
+1980, 775, 795, 103, 104
+1981, 777, 797, 85, 86
+1982, 778, 798, 94, 95
+1983, 782, 90, 91, 801
+1984, 780, 108, 109, 799
+1985, 781, 99, 100, 800
+1986, 779, 81, 82, 802
+1987, 809, 777, 824, 784
+1988, 807, 775, 826, 785
+1989, 810, 778, 825, 786
+1990, 808, 776, 823, 783
+1991, 820, 829, 79, 80
+1992, 822, 828, 88, 89
+1993, 821, 830, 106, 107
+1994, 819, 827, 97, 98
+1995, 805, 790, 821, 780
+1996, 804, 787, 820, 779
+1997, 803, 789, 822, 782
+1998, 806, 788, 819, 781
+1999, 780, 821, 107, 108
+2000, 781, 819, 98, 99
+2001, 779, 820, 80, 81
+2002, 782, 822, 89, 90
+2003, 794, 802, 811, 772
+2004, 792, 800, 812, 773
+2005, 793, 799, 813, 771
+2006, 791, 801, 814, 774
+2007, 791, 774, 816, 798
+2008, 794, 772, 818, 797
+2009, 792, 773, 815, 795
+2010, 793, 771, 817, 796
+2011, 777, 86, 87, 824
+2012, 776, 77, 78, 823
+2013, 778, 95, 96, 825
+2014, 775, 104, 105, 826
+2015, 773, 812, 102, 815
+2016, 771, 813, 75, 817
+2017, 772, 811, 84, 818
+2018, 774, 814, 93, 816
+2019, 102, 103, 795, 815
+2020, 75, 76, 796, 817
+2021, 84, 85, 797, 818
+2022, 93, 94, 798, 816
+2023, 83, 811, 802, 82
+2024, 101, 812, 800, 100
+2025, 110, 813, 799, 109
+2026, 92, 814, 801, 91
+2027, 785, 121, 122, 807
+2028, 783, 116, 117, 808
+2029, 784, 111, 112, 809
+2030, 786, 126, 127, 810
+2031, 788, 806, 124, 125
+2032, 789, 803, 129, 130
+2033, 790, 805, 119, 120
+2034, 787, 804, 114, 115
+2035, 825, 827, 834, 786
+2036, 823, 829, 832, 783
+2037, 826, 830, 833, 785
+2038, 824, 828, 831, 784
+2039, 820, 787, 832, 829
+2040, 819, 788, 834, 827
+2041, 822, 789, 831, 828
+2042, 821, 790, 833, 830
+*Element, type=C3D8, ELSET=Volume1
+2043, 28, 243, 27, 2, 348, 1195, 339, 140
+2044, 348, 1195, 339, 140, 349, 1196, 340, 141
+2045, 349, 1196, 340, 141, 350, 1197, 341, 142
+2046, 350, 1197, 341, 142, 351, 1198, 342, 143
+2047, 351, 1198, 342, 143, 352, 1199, 343, 144
+2048, 352, 1199, 343, 144, 353, 1200, 344, 145
+2049, 353, 1200, 344, 145, 354, 1201, 345, 146
+2050, 354, 1201, 345, 146, 355, 1202, 346, 147
+2051, 355, 1202, 346, 147, 356, 1203, 347, 148
+2052, 356, 1203, 347, 148, 84, 811, 83, 11
+2053, 37, 246, 36, 3, 429, 1222, 420, 149
+2054, 429, 1222, 420, 149, 430, 1223, 421, 150
+2055, 430, 1223, 421, 150, 431, 1224, 422, 151
+2056, 431, 1224, 422, 151, 432, 1225, 423, 152
+2057, 432, 1225, 423, 152, 433, 1226, 424, 153
+2058, 433, 1226, 424, 153, 434, 1227, 425, 154
+2059, 434, 1227, 425, 154, 435, 1228, 426, 155
+2060, 435, 1228, 426, 155, 436, 1229, 427, 156
+2061, 436, 1229, 427, 156, 437, 1230, 428, 157
+2062, 437, 1230, 428, 157, 93, 814, 92, 12
+2063, 46, 244, 45, 4, 510, 1204, 501, 158
+2064, 510, 1204, 501, 158, 511, 1205, 502, 159
+2065, 511, 1205, 502, 159, 512, 1206, 503, 160
+2066, 512, 1206, 503, 160, 513, 1207, 504, 161
+2067, 513, 1207, 504, 161, 514, 1208, 505, 162
+2068, 514, 1208, 505, 162, 515, 1209, 506, 163
+2069, 515, 1209, 506, 163, 516, 1210, 507, 164
+2070, 516, 1210, 507, 164, 517, 1211, 508, 165
+2071, 517, 1211, 508, 165, 518, 1212, 509, 166
+2072, 518, 1212, 509, 166, 102, 812, 101, 13
+2073, 19, 245, 54, 1, 267, 1213, 582, 131
+2074, 267, 1213, 582, 131, 268, 1214, 583, 132
+2075, 268, 1214, 583, 132, 269, 1215, 584, 133
+2076, 269, 1215, 584, 133, 270, 1216, 585, 134
+2077, 270, 1216, 585, 134, 271, 1217, 586, 135
+2078, 271, 1217, 586, 135, 272, 1218, 587, 136
+2079, 272, 1218, 587, 136, 273, 1219, 588, 137
+2080, 273, 1219, 588, 137, 274, 1220, 589, 138
+2081, 274, 1220, 589, 138, 275, 1221, 590, 139
+2082, 275, 1221, 590, 139, 75, 813, 110, 10
+2083, 62, 61, 238, 224, 699, 690, 1150, 1024
+2084, 699, 690, 1150, 1024, 700, 691, 1151, 1025
+2085, 700, 691, 1151, 1025, 701, 692, 1152, 1026
+2086, 701, 692, 1152, 1026, 702, 693, 1153, 1027
+2087, 702, 693, 1153, 1027, 703, 694, 1154, 1028
+2088, 703, 694, 1154, 1028, 704, 695, 1155, 1029
+2089, 704, 695, 1155, 1029, 705, 696, 1156, 1030
+2090, 705, 696, 1156, 1030, 706, 697, 1157, 1031
+2091, 706, 697, 1157, 1031, 707, 698, 1158, 1032
+2092, 707, 698, 1158, 1032, 123, 124, 806, 792
+2093, 57, 56, 235, 223, 744, 735, 1123, 1015
+2094, 744, 735, 1123, 1015, 745, 736, 1124, 1016
+2095, 745, 736, 1124, 1016, 746, 737, 1125, 1017
+2096, 746, 737, 1125, 1017, 747, 738, 1126, 1018
+2097, 747, 738, 1126, 1018, 748, 739, 1127, 1019
+2098, 748, 739, 1127, 1019, 749, 740, 1128, 1020
+2099, 749, 740, 1128, 1020, 750, 741, 1129, 1021
+2100, 750, 741, 1129, 1021, 751, 742, 1130, 1022
+2101, 751, 742, 1130, 1022, 752, 743, 1131, 1023
+2102, 752, 743, 1131, 1023, 128, 129, 803, 791
+2103, 72, 71, 236, 226, 609, 600, 1132, 1042
+2104, 609, 600, 1132, 1042, 610, 601, 1133, 1043
+2105, 610, 601, 1133, 1043, 611, 602, 1134, 1044
+2106, 611, 602, 1134, 1044, 612, 603, 1135, 1045
+2107, 612, 603, 1135, 1045, 613, 604, 1136, 1046
+2108, 613, 604, 1136, 1046, 614, 605, 1137, 1047
+2109, 614, 605, 1137, 1047, 615, 606, 1138, 1048
+2110, 615, 606, 1138, 1048, 616, 607, 1139, 1049
+2111, 616, 607, 1139, 1049, 617, 608, 1140, 1050
+2112, 617, 608, 1140, 1050, 113, 114, 804, 794
+2113, 67, 66, 237, 225, 654, 645, 1141, 1033
+2114, 654, 645, 1141, 1033, 655, 646, 1142, 1034
+2115, 655, 646, 1142, 1034, 656, 647, 1143, 1035
+2116, 656, 647, 1143, 1035, 657, 648, 1144, 1036
+2117, 657, 648, 1144, 1036, 658, 649, 1145, 1037
+2118, 658, 649, 1145, 1037, 659, 650, 1146, 1038
+2119, 659, 650, 1146, 1038, 660, 651, 1147, 1039
+2120, 660, 651, 1147, 1039, 661, 652, 1148, 1040
+2121, 661, 652, 1148, 1040, 662, 653, 1149, 1041
+2122, 662, 653, 1149, 1041, 118, 119, 805, 793
+2123, 67, 225, 240, 68, 654, 1033, 1168, 663
+2124, 654, 1033, 1168, 663, 655, 1034, 1169, 664
+2125, 655, 1034, 1169, 664, 656, 1035, 1170, 665
+2126, 656, 1035, 1170, 665, 657, 1036, 1171, 666
+2127, 657, 1036, 1171, 666, 658, 1037, 1172, 667
+2128, 658, 1037, 1172, 667, 659, 1038, 1173, 668
+2129, 659, 1038, 1173, 668, 660, 1039, 1174, 669
+2130, 660, 1039, 1174, 669, 661, 1040, 1175, 670
+2131, 661, 1040, 1175, 670, 662, 1041, 1176, 671
+2132, 662, 1041, 1176, 671, 118, 793, 808, 117
+2133, 57, 223, 242, 58, 744, 1015, 1186, 753
+2134, 744, 1015, 1186, 753, 745, 1016, 1187, 754
+2135, 745, 1016, 1187, 754, 746, 1017, 1188, 755
+2136, 746, 1017, 1188, 755, 747, 1018, 1189, 756
+2137, 747, 1018, 1189, 756, 748, 1019, 1190, 757
+2138, 748, 1019, 1190, 757, 749, 1020, 1191, 758
+2139, 749, 1020, 1191, 758, 750, 1021, 1192, 759
+2140, 750, 1021, 1192, 759, 751, 1022, 1193, 760
+2141, 751, 1022, 1193, 760, 752, 1023, 1194, 761
+2142, 752, 1023, 1194, 761, 128, 791, 810, 127
+2143, 62, 224, 239, 63, 699, 1024, 1159, 708
+2144, 699, 1024, 1159, 708, 700, 1025, 1160, 709
+2145, 700, 1025, 1160, 709, 701, 1026, 1161, 710
+2146, 701, 1026, 1161, 710, 702, 1027, 1162, 711
+2147, 702, 1027, 1162, 711, 703, 1028, 1163, 712
+2148, 703, 1028, 1163, 712, 704, 1029, 1164, 713
+2149, 704, 1029, 1164, 713, 705, 1030, 1165, 714
+2150, 705, 1030, 1165, 714, 706, 1031, 1166, 715
+2151, 706, 1031, 1166, 715, 707, 1032, 1167, 716
+2152, 707, 1032, 1167, 716, 123, 792, 807, 122
+2153, 72, 226, 241, 73, 609, 1042, 1177, 618
+2154, 609, 1042, 1177, 618, 610, 1043, 1178, 619
+2155, 610, 1043, 1178, 619, 611, 1044, 1179, 620
+2156, 611, 1044, 1179, 620, 612, 1045, 1180, 621
+2157, 612, 1045, 1180, 621, 613, 1046, 1181, 622
+2158, 613, 1046, 1181, 622, 614, 1047, 1182, 623
+2159, 614, 1047, 1182, 623, 615, 1048, 1183, 624
+2160, 615, 1048, 1183, 624, 616, 1049, 1184, 625
+2161, 616, 1049, 1184, 625, 617, 1050, 1185, 626
+2162, 617, 1050, 1185, 626, 113, 794, 809, 112
+2163, 6, 264, 219, 70, 176, 1384, 979, 591
+2164, 176, 1384, 979, 591, 177, 1385, 980, 592
+2165, 177, 1385, 980, 592, 178, 1386, 981, 593
+2166, 178, 1386, 981, 593, 179, 1387, 982, 594
+2167, 179, 1387, 982, 594, 180, 1388, 983, 595
+2168, 180, 1388, 983, 595, 181, 1389, 984, 596
+2169, 181, 1389, 984, 596, 182, 1390, 985, 597
+2170, 182, 1390, 985, 597, 183, 1391, 986, 598
+2171, 183, 1391, 986, 598, 184, 1392, 987, 599
+2172, 184, 1392, 987, 599, 16, 832, 787, 115
+2173, 8, 263, 221, 55, 167, 1375, 997, 726
+2174, 167, 1375, 997, 726, 168, 1376, 998, 727
+2175, 168, 1376, 998, 727, 169, 1377, 999, 728
+2176, 169, 1377, 999, 728, 170, 1378, 1000, 729
+2177, 170, 1378, 1000, 729, 171, 1379, 1001, 730
+2178, 171, 1379, 1001, 730, 172, 1380, 1002, 731
+2179, 172, 1380, 1002, 731, 173, 1381, 1003, 732
+2180, 173, 1381, 1003, 732, 174, 1382, 1004, 733
+2181, 174, 1382, 1004, 733, 175, 1383, 1005, 734
+2182, 175, 1383, 1005, 734, 14, 831, 789, 130
+2183, 7, 266, 220, 60, 194, 1402, 988, 681
+2184, 194, 1402, 988, 681, 195, 1403, 989, 682
+2185, 195, 1403, 989, 682, 196, 1404, 990, 683
+2186, 196, 1404, 990, 683, 197, 1405, 991, 684
+2187, 197, 1405, 991, 684, 198, 1406, 992, 685
+2188, 198, 1406, 992, 685, 199, 1407, 993, 686
+2189, 199, 1407, 993, 686, 200, 1408, 994, 687
+2190, 200, 1408, 994, 687, 201, 1409, 995, 688
+2191, 201, 1409, 995, 688, 202, 1410, 996, 689
+2192, 202, 1410, 996, 689, 18, 834, 788, 125
+2193, 9, 265, 222, 65, 185, 1393, 1006, 636
+2194, 185, 1393, 1006, 636, 186, 1394, 1007, 637
+2195, 186, 1394, 1007, 637, 187, 1395, 1008, 638
+2196, 187, 1395, 1008, 638, 188, 1396, 1009, 639
+2197, 188, 1396, 1009, 639, 189, 1397, 1010, 640
+2198, 189, 1397, 1010, 640, 190, 1398, 1011, 641
+2199, 190, 1398, 1011, 641, 191, 1399, 1012, 642
+2200, 191, 1399, 1012, 642, 192, 1400, 1013, 643
+2201, 192, 1400, 1013, 643, 193, 1401, 1014, 644
+2202, 193, 1401, 1014, 644, 17, 833, 790, 120
+2203, 228, 208, 240, 225, 1060, 880, 1168, 1033
+2204, 1060, 880, 1168, 1033, 1061, 881, 1169, 1034
+2205, 1061, 881, 1169, 1034, 1062, 882, 1170, 1035
+2206, 1062, 882, 1170, 1035, 1063, 883, 1171, 1036
+2207, 1063, 883, 1171, 1036, 1064, 884, 1172, 1037
+2208, 1064, 884, 1172, 1037, 1065, 885, 1173, 1038
+2209, 1065, 885, 1173, 1038, 1066, 886, 1174, 1039
+2210, 1066, 886, 1174, 1039, 1067, 887, 1175, 1040
+2211, 1067, 887, 1175, 1040, 1068, 888, 1176, 1041
+2212, 1068, 888, 1176, 1041, 796, 776, 808, 793
+2213, 230, 210, 242, 223, 1078, 898, 1186, 1015
+2214, 1078, 898, 1186, 1015, 1079, 899, 1187, 1016
+2215, 1079, 899, 1187, 1016, 1080, 900, 1188, 1017
+2216, 1080, 900, 1188, 1017, 1081, 901, 1189, 1018
+2217, 1081, 901, 1189, 1018, 1082, 902, 1190, 1019
+2218, 1082, 902, 1190, 1019, 1083, 903, 1191, 1020
+2219, 1083, 903, 1191, 1020, 1084, 904, 1192, 1021
+2220, 1084, 904, 1192, 1021, 1085, 905, 1193, 1022
+2221, 1085, 905, 1193, 1022, 1086, 906, 1194, 1023
+2222, 1086, 906, 1194, 1023, 798, 778, 810, 791
+2223, 227, 207, 239, 224, 1051, 871, 1159, 1024
+2224, 1051, 871, 1159, 1024, 1052, 872, 1160, 1025
+2225, 1052, 872, 1160, 1025, 1053, 873, 1161, 1026
+2226, 1053, 873, 1161, 1026, 1054, 874, 1162, 1027
+2227, 1054, 874, 1162, 1027, 1055, 875, 1163, 1028
+2228, 1055, 875, 1163, 1028, 1056, 876, 1164, 1029
+2229, 1056, 876, 1164, 1029, 1057, 877, 1165, 1030
+2230, 1057, 877, 1165, 1030, 1058, 878, 1166, 1031
+2231, 1058, 878, 1166, 1031, 1059, 879, 1167, 1032
+2232, 1059, 879, 1167, 1032, 795, 775, 807, 792
+2233, 229, 209, 241, 226, 1069, 889, 1177, 1042
+2234, 1069, 889, 1177, 1042, 1070, 890, 1178, 1043
+2235, 1070, 890, 1178, 1043, 1071, 891, 1179, 1044
+2236, 1071, 891, 1179, 1044, 1072, 892, 1180, 1045
+2237, 1072, 892, 1180, 1045, 1073, 893, 1181, 1046
+2238, 1073, 893, 1181, 1046, 1074, 894, 1182, 1047
+2239, 1074, 894, 1182, 1047, 1075, 895, 1183, 1048
+2240, 1075, 895, 1183, 1048, 1076, 896, 1184, 1049
+2241, 1076, 896, 1184, 1049, 1077, 897, 1185, 1050
+2242, 1077, 897, 1185, 1050, 797, 777, 809, 794
+2243, 231, 225, 237, 212, 1087, 1033, 1141, 916
+2244, 1087, 1033, 1141, 916, 1088, 1034, 1142, 917
+2245, 1088, 1034, 1142, 917, 1089, 1035, 1143, 918
+2246, 1089, 1035, 1143, 918, 1090, 1036, 1144, 919
+2247, 1090, 1036, 1144, 919, 1091, 1037, 1145, 920
+2248, 1091, 1037, 1145, 920, 1092, 1038, 1146, 921
+2249, 1092, 1038, 1146, 921, 1093, 1039, 1147, 922
+2250, 1093, 1039, 1147, 922, 1094, 1040, 1148, 923
+2251, 1094, 1040, 1148, 923, 1095, 1041, 1149, 924
+2252, 1095, 1041, 1149, 924, 799, 793, 805, 780
+2253, 233, 223, 235, 214, 1105, 1015, 1123, 934
+2254, 1105, 1015, 1123, 934, 1106, 1016, 1124, 935
+2255, 1106, 1016, 1124, 935, 1107, 1017, 1125, 936
+2256, 1107, 1017, 1125, 936, 1108, 1018, 1126, 937
+2257, 1108, 1018, 1126, 937, 1109, 1019, 1127, 938
+2258, 1109, 1019, 1127, 938, 1110, 1020, 1128, 939
+2259, 1110, 1020, 1128, 939, 1111, 1021, 1129, 940
+2260, 1111, 1021, 1129, 940, 1112, 1022, 1130, 941
+2261, 1112, 1022, 1130, 941, 1113, 1023, 1131, 942
+2262, 1113, 1023, 1131, 942, 801, 791, 803, 782
+2263, 232, 224, 238, 213, 1096, 1024, 1150, 925
+2264, 1096, 1024, 1150, 925, 1097, 1025, 1151, 926
+2265, 1097, 1025, 1151, 926, 1098, 1026, 1152, 927
+2266, 1098, 1026, 1152, 927, 1099, 1027, 1153, 928
+2267, 1099, 1027, 1153, 928, 1100, 1028, 1154, 929
+2268, 1100, 1028, 1154, 929, 1101, 1029, 1155, 930
+2269, 1101, 1029, 1155, 930, 1102, 1030, 1156, 931
+2270, 1102, 1030, 1156, 931, 1103, 1031, 1157, 932
+2271, 1103, 1031, 1157, 932, 1104, 1032, 1158, 933
+2272, 1104, 1032, 1158, 933, 800, 792, 806, 781
+2273, 234, 226, 236, 211, 1114, 1042, 1132, 907
+2274, 1114, 1042, 1132, 907, 1115, 1043, 1133, 908
+2275, 1115, 1043, 1133, 908, 1116, 1044, 1134, 909
+2276, 1116, 1044, 1134, 909, 1117, 1045, 1135, 910
+2277, 1117, 1045, 1135, 910, 1118, 1046, 1136, 911
+2278, 1118, 1046, 1136, 911, 1119, 1047, 1137, 912
+2279, 1119, 1047, 1137, 912, 1120, 1048, 1138, 913
+2280, 1120, 1048, 1138, 913, 1121, 1049, 1139, 914
+2281, 1121, 1049, 1139, 914, 1122, 1050, 1140, 915
+2282, 1122, 1050, 1140, 915, 802, 794, 804, 779
+2283, 217, 265, 9, 64, 961, 1393, 185, 717
+2284, 961, 1393, 185, 717, 962, 1394, 186, 718
+2285, 962, 1394, 186, 718, 963, 1395, 187, 719
+2286, 963, 1395, 187, 719, 964, 1396, 188, 720
+2287, 964, 1396, 188, 720, 965, 1397, 189, 721
+2288, 965, 1397, 189, 721, 966, 1398, 190, 722
+2289, 966, 1398, 190, 722, 967, 1399, 191, 723
+2290, 967, 1399, 191, 723, 968, 1400, 192, 724
+2291, 968, 1400, 192, 724, 969, 1401, 193, 725
+2292, 969, 1401, 193, 725, 785, 833, 17, 121
+2293, 218, 266, 7, 59, 970, 1402, 194, 762
+2294, 970, 1402, 194, 762, 971, 1403, 195, 763
+2295, 971, 1403, 195, 763, 972, 1404, 196, 764
+2296, 972, 1404, 196, 764, 973, 1405, 197, 765
+2297, 973, 1405, 197, 765, 974, 1406, 198, 766
+2298, 974, 1406, 198, 766, 975, 1407, 199, 767
+2299, 975, 1407, 199, 767, 976, 1408, 200, 768
+2300, 976, 1408, 200, 768, 977, 1409, 201, 769
+2301, 977, 1409, 201, 769, 978, 1410, 202, 770
+2302, 978, 1410, 202, 770, 786, 834, 18, 126
+2303, 216, 263, 8, 74, 952, 1375, 167, 627
+2304, 952, 1375, 167, 627, 953, 1376, 168, 628
+2305, 953, 1376, 168, 628, 954, 1377, 169, 629
+2306, 954, 1377, 169, 629, 955, 1378, 170, 630
+2307, 955, 1378, 170, 630, 956, 1379, 171, 631
+2308, 956, 1379, 171, 631, 957, 1380, 172, 632
+2309, 957, 1380, 172, 632, 958, 1381, 173, 633
+2310, 958, 1381, 173, 633, 959, 1382, 174, 634
+2311, 959, 1382, 174, 634, 960, 1383, 175, 635
+2312, 960, 1383, 175, 635, 784, 831, 14, 111
+2313, 215, 264, 6, 69, 943, 1384, 176, 672
+2314, 943, 1384, 176, 672, 944, 1385, 177, 673
+2315, 944, 1385, 177, 673, 945, 1386, 178, 674
+2316, 945, 1386, 178, 674, 946, 1387, 179, 675
+2317, 946, 1387, 179, 675, 947, 1388, 180, 676
+2318, 947, 1388, 180, 676, 948, 1389, 181, 677
+2319, 948, 1389, 181, 677, 949, 1390, 182, 678
+2320, 949, 1390, 182, 678, 950, 1391, 183, 679
+2321, 950, 1391, 183, 679, 951, 1392, 184, 680
+2322, 951, 1392, 184, 680, 783, 832, 16, 116
+2323, 258, 49, 50, 262, 1330, 537, 546, 1366
+2324, 1330, 537, 546, 1366, 1331, 538, 547, 1367
+2325, 1331, 538, 547, 1367, 1332, 539, 548, 1368
+2326, 1332, 539, 548, 1368, 1333, 540, 549, 1369
+2327, 1333, 540, 549, 1369, 1334, 541, 550, 1370
+2328, 1334, 541, 550, 1370, 1335, 542, 551, 1371
+2329, 1335, 542, 551, 1371, 1336, 543, 552, 1372
+2330, 1336, 543, 552, 1372, 1337, 544, 553, 1373
+2331, 1337, 544, 553, 1373, 1338, 545, 554, 1374
+2332, 1338, 545, 554, 1374, 826, 105, 106, 830
+2333, 257, 40, 41, 259, 1321, 456, 465, 1339
+2334, 1321, 456, 465, 1339, 1322, 457, 466, 1340
+2335, 1322, 457, 466, 1340, 1323, 458, 467, 1341
+2336, 1323, 458, 467, 1341, 1324, 459, 468, 1342
+2337, 1324, 459, 468, 1342, 1325, 460, 469, 1343
+2338, 1325, 460, 469, 1343, 1326, 461, 470, 1344
+2339, 1326, 461, 470, 1344, 1327, 462, 471, 1345
+2340, 1327, 462, 471, 1345, 1328, 463, 472, 1346
+2341, 1328, 463, 472, 1346, 1329, 464, 473, 1347
+2342, 1329, 464, 473, 1347, 825, 96, 97, 827
+2343, 256, 31, 32, 260, 1312, 375, 384, 1348
+2344, 1312, 375, 384, 1348, 1313, 376, 385, 1349
+2345, 1313, 376, 385, 1349, 1314, 377, 386, 1350
+2346, 1314, 377, 386, 1350, 1315, 378, 387, 1351
+2347, 1315, 378, 387, 1351, 1316, 379, 388, 1352
+2348, 1316, 379, 388, 1352, 1317, 380, 389, 1353
+2349, 1317, 380, 389, 1353, 1318, 381, 390, 1354
+2350, 1318, 381, 390, 1354, 1319, 382, 391, 1355
+2351, 1319, 382, 391, 1355, 1320, 383, 392, 1356
+2352, 1320, 383, 392, 1356, 824, 87, 88, 828
+2353, 255, 22, 23, 261, 1303, 294, 303, 1357
+2354, 1303, 294, 303, 1357, 1304, 295, 304, 1358
+2355, 1304, 295, 304, 1358, 1305, 296, 305, 1359
+2356, 1305, 296, 305, 1359, 1306, 297, 306, 1360
+2357, 1306, 297, 306, 1360, 1307, 298, 307, 1361
+2358, 1307, 298, 307, 1361, 1308, 299, 308, 1362
+2359, 1308, 299, 308, 1362, 1309, 300, 309, 1363
+2360, 1309, 300, 309, 1363, 1310, 301, 310, 1364
+2361, 1310, 301, 310, 1364, 1311, 302, 311, 1365
+2362, 1311, 302, 311, 1365, 823, 78, 79, 829
+2363, 208, 228, 20, 21, 880, 1060, 276, 285
+2364, 880, 1060, 276, 285, 881, 1061, 277, 286
+2365, 881, 1061, 277, 286, 882, 1062, 278, 287
+2366, 882, 1062, 278, 287, 883, 1063, 279, 288
+2367, 883, 1063, 279, 288, 884, 1064, 280, 289
+2368, 884, 1064, 280, 289, 885, 1065, 281, 290
+2369, 885, 1065, 281, 290, 886, 1066, 282, 291
+2370, 886, 1066, 282, 291, 887, 1067, 283, 292
+2371, 887, 1067, 283, 292, 888, 1068, 284, 293
+2372, 888, 1068, 284, 293, 776, 796, 76, 77
+2373, 207, 227, 47, 48, 871, 1051, 519, 528
+2374, 871, 1051, 519, 528, 872, 1052, 520, 529
+2375, 872, 1052, 520, 529, 873, 1053, 521, 530
+2376, 873, 1053, 521, 530, 874, 1054, 522, 531
+2377, 874, 1054, 522, 531, 875, 1055, 523, 532
+2378, 875, 1055, 523, 532, 876, 1056, 524, 533
+2379, 876, 1056, 524, 533, 877, 1057, 525, 534
+2380, 877, 1057, 525, 534, 878, 1058, 526, 535
+2381, 878, 1058, 526, 535, 879, 1059, 527, 536
+2382, 879, 1059, 527, 536, 775, 795, 103, 104
+2383, 209, 229, 29, 30, 889, 1069, 357, 366
+2384, 889, 1069, 357, 366, 890, 1070, 358, 367
+2385, 890, 1070, 358, 367, 891, 1071, 359, 368
+2386, 891, 1071, 359, 368, 892, 1072, 360, 369
+2387, 892, 1072, 360, 369, 893, 1073, 361, 370
+2388, 893, 1073, 361, 370, 894, 1074, 362, 371
+2389, 894, 1074, 362, 371, 895, 1075, 363, 372
+2390, 895, 1075, 363, 372, 896, 1076, 364, 373
+2391, 896, 1076, 364, 373, 897, 1077, 365, 374
+2392, 897, 1077, 365, 374, 777, 797, 85, 86
+2393, 210, 230, 38, 39, 898, 1078, 438, 447
+2394, 898, 1078, 438, 447, 899, 1079, 439, 448
+2395, 899, 1079, 439, 448, 900, 1080, 440, 449
+2396, 900, 1080, 440, 449, 901, 1081, 441, 450
+2397, 901, 1081, 441, 450, 902, 1082, 442, 451
+2398, 902, 1082, 442, 451, 903, 1083, 443, 452
+2399, 903, 1083, 443, 452, 904, 1084, 444, 453
+2400, 904, 1084, 444, 453, 905, 1085, 445, 454
+2401, 905, 1085, 445, 454, 906, 1086, 446, 455
+2402, 906, 1086, 446, 455, 778, 798, 94, 95
+2403, 214, 34, 35, 233, 934, 402, 411, 1105
+2404, 934, 402, 411, 1105, 935, 403, 412, 1106
+2405, 935, 403, 412, 1106, 936, 404, 413, 1107
+2406, 936, 404, 413, 1107, 937, 405, 414, 1108
+2407, 937, 405, 414, 1108, 938, 406, 415, 1109
+2408, 938, 406, 415, 1109, 939, 407, 416, 1110
+2409, 939, 407, 416, 1110, 940, 408, 417, 1111
+2410, 940, 408, 417, 1111, 941, 409, 418, 1112
+2411, 941, 409, 418, 1112, 942, 410, 419, 1113
+2412, 942, 410, 419, 1113, 782, 90, 91, 801
+2413, 212, 52, 53, 231, 916, 564, 573, 1087
+2414, 916, 564, 573, 1087, 917, 565, 574, 1088
+2415, 917, 565, 574, 1088, 918, 566, 575, 1089
+2416, 918, 566, 575, 1089, 919, 567, 576, 1090
+2417, 919, 567, 576, 1090, 920, 568, 577, 1091
+2418, 920, 568, 577, 1091, 921, 569, 578, 1092
+2419, 921, 569, 578, 1092, 922, 570, 579, 1093
+2420, 922, 570, 579, 1093, 923, 571, 580, 1094
+2421, 923, 571, 580, 1094, 924, 572, 581, 1095
+2422, 924, 572, 581, 1095, 780, 108, 109, 799
+2423, 213, 43, 44, 232, 925, 483, 492, 1096
+2424, 925, 483, 492, 1096, 926, 484, 493, 1097
+2425, 926, 484, 493, 1097, 927, 485, 494, 1098
+2426, 927, 485, 494, 1098, 928, 486, 495, 1099
+2427, 928, 486, 495, 1099, 929, 487, 496, 1100
+2428, 929, 487, 496, 1100, 930, 488, 497, 1101
+2429, 930, 488, 497, 1101, 931, 489, 498, 1102
+2430, 931, 489, 498, 1102, 932, 490, 499, 1103
+2431, 932, 490, 499, 1103, 933, 491, 500, 1104
+2432, 933, 491, 500, 1104, 781, 99, 100, 800
+2433, 211, 25, 26, 234, 907, 321, 330, 1114
+2434, 907, 321, 330, 1114, 908, 322, 331, 1115
+2435, 908, 322, 331, 1115, 909, 323, 332, 1116
+2436, 909, 323, 332, 1116, 910, 324, 333, 1117
+2437, 910, 324, 333, 1117, 911, 325, 334, 1118
+2438, 911, 325, 334, 1118, 912, 326, 335, 1119
+2439, 912, 326, 335, 1119, 913, 327, 336, 1120
+2440, 913, 327, 336, 1120, 914, 328, 337, 1121
+2441, 914, 328, 337, 1121, 915, 329, 338, 1122
+2442, 915, 329, 338, 1122, 779, 81, 82, 802
+2443, 241, 209, 256, 216, 1177, 889, 1312, 952
+2444, 1177, 889, 1312, 952, 1178, 890, 1313, 953
+2445, 1178, 890, 1313, 953, 1179, 891, 1314, 954
+2446, 1179, 891, 1314, 954, 1180, 892, 1315, 955
+2447, 1180, 892, 1315, 955, 1181, 893, 1316, 956
+2448, 1181, 893, 1316, 956, 1182, 894, 1317, 957
+2449, 1182, 894, 1317, 957, 1183, 895, 1318, 958
+2450, 1183, 895, 1318, 958, 1184, 896, 1319, 959
+2451, 1184, 896, 1319, 959, 1185, 897, 1320, 960
+2452, 1185, 897, 1320, 960, 809, 777, 824, 784
+2453, 239, 207, 258, 217, 1159, 871, 1330, 961
+2454, 1159, 871, 1330, 961, 1160, 872, 1331, 962
+2455, 1160, 872, 1331, 962, 1161, 873, 1332, 963
+2456, 1161, 873, 1332, 963, 1162, 874, 1333, 964
+2457, 1162, 874, 1333, 964, 1163, 875, 1334, 965
+2458, 1163, 875, 1334, 965, 1164, 876, 1335, 966
+2459, 1164, 876, 1335, 966, 1165, 877, 1336, 967
+2460, 1165, 877, 1336, 967, 1166, 878, 1337, 968
+2461, 1166, 878, 1337, 968, 1167, 879, 1338, 969
+2462, 1167, 879, 1338, 969, 807, 775, 826, 785
+2463, 242, 210, 257, 218, 1186, 898, 1321, 970
+2464, 1186, 898, 1321, 970, 1187, 899, 1322, 971
+2465, 1187, 899, 1322, 971, 1188, 900, 1323, 972
+2466, 1188, 900, 1323, 972, 1189, 901, 1324, 973
+2467, 1189, 901, 1324, 973, 1190, 902, 1325, 974
+2468, 1190, 902, 1325, 974, 1191, 903, 1326, 975
+2469, 1191, 903, 1326, 975, 1192, 904, 1327, 976
+2470, 1192, 904, 1327, 976, 1193, 905, 1328, 977
+2471, 1193, 905, 1328, 977, 1194, 906, 1329, 978
+2472, 1194, 906, 1329, 978, 810, 778, 825, 786
+2473, 240, 208, 255, 215, 1168, 880, 1303, 943
+2474, 1168, 880, 1303, 943, 1169, 881, 1304, 944
+2475, 1169, 881, 1304, 944, 1170, 882, 1305, 945
+2476, 1170, 882, 1305, 945, 1171, 883, 1306, 946
+2477, 1171, 883, 1306, 946, 1172, 884, 1307, 947
+2478, 1172, 884, 1307, 947, 1173, 885, 1308, 948
+2479, 1173, 885, 1308, 948, 1174, 886, 1309, 949
+2480, 1174, 886, 1309, 949, 1175, 887, 1310, 950
+2481, 1175, 887, 1310, 950, 1176, 888, 1311, 951
+2482, 1176, 888, 1311, 951, 808, 776, 823, 783
+2483, 252, 261, 23, 24, 1276, 1357, 303, 312
+2484, 1276, 1357, 303, 312, 1277, 1358, 304, 313
+2485, 1277, 1358, 304, 313, 1278, 1359, 305, 314
+2486, 1278, 1359, 305, 314, 1279, 1360, 306, 315
+2487, 1279, 1360, 306, 315, 1280, 1361, 307, 316
+2488, 1280, 1361, 307, 316, 1281, 1362, 308, 317
+2489, 1281, 1362, 308, 317, 1282, 1363, 309, 318
+2490, 1282, 1363, 309, 318, 1283, 1364, 310, 319
+2491, 1283, 1364, 310, 319, 1284, 1365, 311, 320
+2492, 1284, 1365, 311, 320, 820, 829, 79, 80
+2493, 254, 260, 32, 33, 1294, 1348, 384, 393
+2494, 1294, 1348, 384, 393, 1295, 1349, 385, 394
+2495, 1295, 1349, 385, 394, 1296, 1350, 386, 395
+2496, 1296, 1350, 386, 395, 1297, 1351, 387, 396
+2497, 1297, 1351, 387, 396, 1298, 1352, 388, 397
+2498, 1298, 1352, 388, 397, 1299, 1353, 389, 398
+2499, 1299, 1353, 389, 398, 1300, 1354, 390, 399
+2500, 1300, 1354, 390, 399, 1301, 1355, 391, 400
+2501, 1301, 1355, 391, 400, 1302, 1356, 392, 401
+2502, 1302, 1356, 392, 401, 822, 828, 88, 89
+2503, 253, 262, 50, 51, 1285, 1366, 546, 555
+2504, 1285, 1366, 546, 555, 1286, 1367, 547, 556
+2505, 1286, 1367, 547, 556, 1287, 1368, 548, 557
+2506, 1287, 1368, 548, 557, 1288, 1369, 549, 558
+2507, 1288, 1369, 549, 558, 1289, 1370, 550, 559
+2508, 1289, 1370, 550, 559, 1290, 1371, 551, 560
+2509, 1290, 1371, 551, 560, 1291, 1372, 552, 561
+2510, 1291, 1372, 552, 561, 1292, 1373, 553, 562
+2511, 1292, 1373, 553, 562, 1293, 1374, 554, 563
+2512, 1293, 1374, 554, 563, 821, 830, 106, 107
+2513, 251, 259, 41, 42, 1267, 1339, 465, 474
+2514, 1267, 1339, 465, 474, 1268, 1340, 466, 475
+2515, 1268, 1340, 466, 475, 1269, 1341, 467, 476
+2516, 1269, 1341, 467, 476, 1270, 1342, 468, 477
+2517, 1270, 1342, 468, 477, 1271, 1343, 469, 478
+2518, 1271, 1343, 469, 478, 1272, 1344, 470, 479
+2519, 1272, 1344, 470, 479, 1273, 1345, 471, 480
+2520, 1273, 1345, 471, 480, 1274, 1346, 472, 481
+2521, 1274, 1346, 472, 481, 1275, 1347, 473, 482
+2522, 1275, 1347, 473, 482, 819, 827, 97, 98
+2523, 237, 222, 253, 212, 1141, 1006, 1285, 916
+2524, 1141, 1006, 1285, 916, 1142, 1007, 1286, 917
+2525, 1142, 1007, 1286, 917, 1143, 1008, 1287, 918
+2526, 1143, 1008, 1287, 918, 1144, 1009, 1288, 919
+2527, 1144, 1009, 1288, 919, 1145, 1010, 1289, 920
+2528, 1145, 1010, 1289, 920, 1146, 1011, 1290, 921
+2529, 1146, 1011, 1290, 921, 1147, 1012, 1291, 922
+2530, 1147, 1012, 1291, 922, 1148, 1013, 1292, 923
+2531, 1148, 1013, 1292, 923, 1149, 1014, 1293, 924
+2532, 1149, 1014, 1293, 924, 805, 790, 821, 780
+2533, 236, 219, 252, 211, 1132, 979, 1276, 907
+2534, 1132, 979, 1276, 907, 1133, 980, 1277, 908
+2535, 1133, 980, 1277, 908, 1134, 981, 1278, 909
+2536, 1134, 981, 1278, 909, 1135, 982, 1279, 910
+2537, 1135, 982, 1279, 910, 1136, 983, 1280, 911
+2538, 1136, 983, 1280, 911, 1137, 984, 1281, 912
+2539, 1137, 984, 1281, 912, 1138, 985, 1282, 913
+2540, 1138, 985, 1282, 913, 1139, 986, 1283, 914
+2541, 1139, 986, 1283, 914, 1140, 987, 1284, 915
+2542, 1140, 987, 1284, 915, 804, 787, 820, 779
+2543, 235, 221, 254, 214, 1123, 997, 1294, 934
+2544, 1123, 997, 1294, 934, 1124, 998, 1295, 935
+2545, 1124, 998, 1295, 935, 1125, 999, 1296, 936
+2546, 1125, 999, 1296, 936, 1126, 1000, 1297, 937
+2547, 1126, 1000, 1297, 937, 1127, 1001, 1298, 938
+2548, 1127, 1001, 1298, 938, 1128, 1002, 1299, 939
+2549, 1128, 1002, 1299, 939, 1129, 1003, 1300, 940
+2550, 1129, 1003, 1300, 940, 1130, 1004, 1301, 941
+2551, 1130, 1004, 1301, 941, 1131, 1005, 1302, 942
+2552, 1131, 1005, 1302, 942, 803, 789, 822, 782
+2553, 238, 220, 251, 213, 1150, 988, 1267, 925
+2554, 1150, 988, 1267, 925, 1151, 989, 1268, 926
+2555, 1151, 989, 1268, 926, 1152, 990, 1269, 927
+2556, 1152, 990, 1269, 927, 1153, 991, 1270, 928
+2557, 1153, 991, 1270, 928, 1154, 992, 1271, 929
+2558, 1154, 992, 1271, 929, 1155, 993, 1272, 930
+2559, 1155, 993, 1272, 930, 1156, 994, 1273, 931
+2560, 1156, 994, 1273, 931, 1157, 995, 1274, 932
+2561, 1157, 995, 1274, 932, 1158, 996, 1275, 933
+2562, 1158, 996, 1275, 933, 806, 788, 819, 781
+2563, 212, 253, 51, 52, 916, 1285, 555, 564
+2564, 916, 1285, 555, 564, 917, 1286, 556, 565
+2565, 917, 1286, 556, 565, 918, 1287, 557, 566
+2566, 918, 1287, 557, 566, 919, 1288, 558, 567
+2567, 919, 1288, 558, 567, 920, 1289, 559, 568
+2568, 920, 1289, 559, 568, 921, 1290, 560, 569
+2569, 921, 1290, 560, 569, 922, 1291, 561, 570
+2570, 922, 1291, 561, 570, 923, 1292, 562, 571
+2571, 923, 1292, 562, 571, 924, 1293, 563, 572
+2572, 924, 1293, 563, 572, 780, 821, 107, 108
+2573, 213, 251, 42, 43, 925, 1267, 474, 483
+2574, 925, 1267, 474, 483, 926, 1268, 475, 484
+2575, 926, 1268, 475, 484, 927, 1269, 476, 485
+2576, 927, 1269, 476, 485, 928, 1270, 477, 486
+2577, 928, 1270, 477, 486, 929, 1271, 478, 487
+2578, 929, 1271, 478, 487, 930, 1272, 479, 488
+2579, 930, 1272, 479, 488, 931, 1273, 480, 489
+2580, 931, 1273, 480, 489, 932, 1274, 481, 490
+2581, 932, 1274, 481, 490, 933, 1275, 482, 491
+2582, 933, 1275, 482, 491, 781, 819, 98, 99
+2583, 211, 252, 24, 25, 907, 1276, 312, 321
+2584, 907, 1276, 312, 321, 908, 1277, 313, 322
+2585, 908, 1277, 313, 322, 909, 1278, 314, 323
+2586, 909, 1278, 314, 323, 910, 1279, 315, 324
+2587, 910, 1279, 315, 324, 911, 1280, 316, 325
+2588, 911, 1280, 316, 325, 912, 1281, 317, 326
+2589, 912, 1281, 317, 326, 913, 1282, 318, 327
+2590, 913, 1282, 318, 327, 914, 1283, 319, 328
+2591, 914, 1283, 319, 328, 915, 1284, 320, 329
+2592, 915, 1284, 320, 329, 779, 820, 80, 81
+2593, 214, 254, 33, 34, 934, 1294, 393, 402
+2594, 934, 1294, 393, 402, 935, 1295, 394, 403
+2595, 935, 1295, 394, 403, 936, 1296, 395, 404
+2596, 936, 1296, 395, 404, 937, 1297, 396, 405
+2597, 937, 1297, 396, 405, 938, 1298, 397, 406
+2598, 938, 1298, 397, 406, 939, 1299, 398, 407
+2599, 939, 1299, 398, 407, 940, 1300, 399, 408
+2600, 940, 1300, 399, 408, 941, 1301, 400, 409
+2601, 941, 1301, 400, 409, 942, 1302, 401, 410
+2602, 942, 1302, 401, 410, 782, 822, 89, 90
+2603, 226, 234, 243, 204, 1042, 1114, 1195, 844
+2604, 1042, 1114, 1195, 844, 1043, 1115, 1196, 845
+2605, 1043, 1115, 1196, 845, 1044, 1116, 1197, 846
+2606, 1044, 1116, 1197, 846, 1045, 1117, 1198, 847
+2607, 1045, 1117, 1198, 847, 1046, 1118, 1199, 848
+2608, 1046, 1118, 1199, 848, 1047, 1119, 1200, 849
+2609, 1047, 1119, 1200, 849, 1048, 1120, 1201, 850
+2610, 1048, 1120, 1201, 850, 1049, 1121, 1202, 851
+2611, 1049, 1121, 1202, 851, 1050, 1122, 1203, 852
+2612, 1050, 1122, 1203, 852, 794, 802, 811, 772
+2613, 224, 232, 244, 205, 1024, 1096, 1204, 853
+2614, 1024, 1096, 1204, 853, 1025, 1097, 1205, 854
+2615, 1025, 1097, 1205, 854, 1026, 1098, 1206, 855
+2616, 1026, 1098, 1206, 855, 1027, 1099, 1207, 856
+2617, 1027, 1099, 1207, 856, 1028, 1100, 1208, 857
+2618, 1028, 1100, 1208, 857, 1029, 1101, 1209, 858
+2619, 1029, 1101, 1209, 858, 1030, 1102, 1210, 859
+2620, 1030, 1102, 1210, 859, 1031, 1103, 1211, 860
+2621, 1031, 1103, 1211, 860, 1032, 1104, 1212, 861
+2622, 1032, 1104, 1212, 861, 792, 800, 812, 773
+2623, 225, 231, 245, 203, 1033, 1087, 1213, 835
+2624, 1033, 1087, 1213, 835, 1034, 1088, 1214, 836
+2625, 1034, 1088, 1214, 836, 1035, 1089, 1215, 837
+2626, 1035, 1089, 1215, 837, 1036, 1090, 1216, 838
+2627, 1036, 1090, 1216, 838, 1037, 1091, 1217, 839
+2628, 1037, 1091, 1217, 839, 1038, 1092, 1218, 840
+2629, 1038, 1092, 1218, 840, 1039, 1093, 1219, 841
+2630, 1039, 1093, 1219, 841, 1040, 1094, 1220, 842
+2631, 1040, 1094, 1220, 842, 1041, 1095, 1221, 843
+2632, 1041, 1095, 1221, 843, 793, 799, 813, 771
+2633, 223, 233, 246, 206, 1015, 1105, 1222, 862
+2634, 1015, 1105, 1222, 862, 1016, 1106, 1223, 863
+2635, 1016, 1106, 1223, 863, 1017, 1107, 1224, 864
+2636, 1017, 1107, 1224, 864, 1018, 1108, 1225, 865
+2637, 1018, 1108, 1225, 865, 1019, 1109, 1226, 866
+2638, 1019, 1109, 1226, 866, 1020, 1110, 1227, 867
+2639, 1020, 1110, 1227, 867, 1021, 1111, 1228, 868
+2640, 1021, 1111, 1228, 868, 1022, 1112, 1229, 869
+2641, 1022, 1112, 1229, 869, 1023, 1113, 1230, 870
+2642, 1023, 1113, 1230, 870, 791, 801, 814, 774
+2643, 223, 206, 248, 230, 1015, 862, 1240, 1078
+2644, 1015, 862, 1240, 1078, 1016, 863, 1241, 1079
+2645, 1016, 863, 1241, 1079, 1017, 864, 1242, 1080
+2646, 1017, 864, 1242, 1080, 1018, 865, 1243, 1081
+2647, 1018, 865, 1243, 1081, 1019, 866, 1244, 1082
+2648, 1019, 866, 1244, 1082, 1020, 867, 1245, 1083
+2649, 1020, 867, 1245, 1083, 1021, 868, 1246, 1084
+2650, 1021, 868, 1246, 1084, 1022, 869, 1247, 1085
+2651, 1022, 869, 1247, 1085, 1023, 870, 1248, 1086
+2652, 1023, 870, 1248, 1086, 791, 774, 816, 798
+2653, 226, 204, 250, 229, 1042, 844, 1258, 1069
+2654, 1042, 844, 1258, 1069, 1043, 845, 1259, 1070
+2655, 1043, 845, 1259, 1070, 1044, 846, 1260, 1071
+2656, 1044, 846, 1260, 1071, 1045, 847, 1261, 1072
+2657, 1045, 847, 1261, 1072, 1046, 848, 1262, 1073
+2658, 1046, 848, 1262, 1073, 1047, 849, 1263, 1074
+2659, 1047, 849, 1263, 1074, 1048, 850, 1264, 1075
+2660, 1048, 850, 1264, 1075, 1049, 851, 1265, 1076
+2661, 1049, 851, 1265, 1076, 1050, 852, 1266, 1077
+2662, 1050, 852, 1266, 1077, 794, 772, 818, 797
+2663, 224, 205, 247, 227, 1024, 853, 1231, 1051
+2664, 1024, 853, 1231, 1051, 1025, 854, 1232, 1052
+2665, 1025, 854, 1232, 1052, 1026, 855, 1233, 1053
+2666, 1026, 855, 1233, 1053, 1027, 856, 1234, 1054
+2667, 1027, 856, 1234, 1054, 1028, 857, 1235, 1055
+2668, 1028, 857, 1235, 1055, 1029, 858, 1236, 1056
+2669, 1029, 858, 1236, 1056, 1030, 859, 1237, 1057
+2670, 1030, 859, 1237, 1057, 1031, 860, 1238, 1058
+2671, 1031, 860, 1238, 1058, 1032, 861, 1239, 1059
+2672, 1032, 861, 1239, 1059, 792, 773, 815, 795
+2673, 225, 203, 249, 228, 1033, 835, 1249, 1060
+2674, 1033, 835, 1249, 1060, 1034, 836, 1250, 1061
+2675, 1034, 836, 1250, 1061, 1035, 837, 1251, 1062
+2676, 1035, 837, 1251, 1062, 1036, 838, 1252, 1063
+2677, 1036, 838, 1252, 1063, 1037, 839, 1253, 1064
+2678, 1037, 839, 1253, 1064, 1038, 840, 1254, 1065
+2679, 1038, 840, 1254, 1065, 1039, 841, 1255, 1066
+2680, 1039, 841, 1255, 1066, 1040, 842, 1256, 1067
+2681, 1040, 842, 1256, 1067, 1041, 843, 1257, 1068
+2682, 1041, 843, 1257, 1068, 793, 771, 817, 796
+2683, 209, 30, 31, 256, 889, 366, 375, 1312
+2684, 889, 366, 375, 1312, 890, 367, 376, 1313
+2685, 890, 367, 376, 1313, 891, 368, 377, 1314
+2686, 891, 368, 377, 1314, 892, 369, 378, 1315
+2687, 892, 369, 378, 1315, 893, 370, 379, 1316
+2688, 893, 370, 379, 1316, 894, 371, 380, 1317
+2689, 894, 371, 380, 1317, 895, 372, 381, 1318
+2690, 895, 372, 381, 1318, 896, 373, 382, 1319
+2691, 896, 373, 382, 1319, 897, 374, 383, 1320
+2692, 897, 374, 383, 1320, 777, 86, 87, 824
+2693, 208, 21, 22, 255, 880, 285, 294, 1303
+2694, 880, 285, 294, 1303, 881, 286, 295, 1304
+2695, 881, 286, 295, 1304, 882, 287, 296, 1305
+2696, 882, 287, 296, 1305, 883, 288, 297, 1306
+2697, 883, 288, 297, 1306, 884, 289, 298, 1307
+2698, 884, 289, 298, 1307, 885, 290, 299, 1308
+2699, 885, 290, 299, 1308, 886, 291, 300, 1309
+2700, 886, 291, 300, 1309, 887, 292, 301, 1310
+2701, 887, 292, 301, 1310, 888, 293, 302, 1311
+2702, 888, 293, 302, 1311, 776, 77, 78, 823
+2703, 210, 39, 40, 257, 898, 447, 456, 1321
+2704, 898, 447, 456, 1321, 899, 448, 457, 1322
+2705, 899, 448, 457, 1322, 900, 449, 458, 1323
+2706, 900, 449, 458, 1323, 901, 450, 459, 1324
+2707, 901, 450, 459, 1324, 902, 451, 460, 1325
+2708, 902, 451, 460, 1325, 903, 452, 461, 1326
+2709, 903, 452, 461, 1326, 904, 453, 462, 1327
+2710, 904, 453, 462, 1327, 905, 454, 463, 1328
+2711, 905, 454, 463, 1328, 906, 455, 464, 1329
+2712, 906, 455, 464, 1329, 778, 95, 96, 825
+2713, 207, 48, 49, 258, 871, 528, 537, 1330
+2714, 871, 528, 537, 1330, 872, 529, 538, 1331
+2715, 872, 529, 538, 1331, 873, 530, 539, 1332
+2716, 873, 530, 539, 1332, 874, 531, 540, 1333
+2717, 874, 531, 540, 1333, 875, 532, 541, 1334
+2718, 875, 532, 541, 1334, 876, 533, 542, 1335
+2719, 876, 533, 542, 1335, 877, 534, 543, 1336
+2720, 877, 534, 543, 1336, 878, 535, 544, 1337
+2721, 878, 535, 544, 1337, 879, 536, 545, 1338
+2722, 879, 536, 545, 1338, 775, 104, 105, 826
+2723, 205, 244, 46, 247, 853, 1204, 510, 1231
+2724, 853, 1204, 510, 1231, 854, 1205, 511, 1232
+2725, 854, 1205, 511, 1232, 855, 1206, 512, 1233
+2726, 855, 1206, 512, 1233, 856, 1207, 513, 1234
+2727, 856, 1207, 513, 1234, 857, 1208, 514, 1235
+2728, 857, 1208, 514, 1235, 858, 1209, 515, 1236
+2729, 858, 1209, 515, 1236, 859, 1210, 516, 1237
+2730, 859, 1210, 516, 1237, 860, 1211, 517, 1238
+2731, 860, 1211, 517, 1238, 861, 1212, 518, 1239
+2732, 861, 1212, 518, 1239, 773, 812, 102, 815
+2733, 203, 245, 19, 249, 835, 1213, 267, 1249
+2734, 835, 1213, 267, 1249, 836, 1214, 268, 1250
+2735, 836, 1214, 268, 1250, 837, 1215, 269, 1251
+2736, 837, 1215, 269, 1251, 838, 1216, 270, 1252
+2737, 838, 1216, 270, 1252, 839, 1217, 271, 1253
+2738, 839, 1217, 271, 1253, 840, 1218, 272, 1254
+2739, 840, 1218, 272, 1254, 841, 1219, 273, 1255
+2740, 841, 1219, 273, 1255, 842, 1220, 274, 1256
+2741, 842, 1220, 274, 1256, 843, 1221, 275, 1257
+2742, 843, 1221, 275, 1257, 771, 813, 75, 817
+2743, 204, 243, 28, 250, 844, 1195, 348, 1258
+2744, 844, 1195, 348, 1258, 845, 1196, 349, 1259
+2745, 845, 1196, 349, 1259, 846, 1197, 350, 1260
+2746, 846, 1197, 350, 1260, 847, 1198, 351, 1261
+2747, 847, 1198, 351, 1261, 848, 1199, 352, 1262
+2748, 848, 1199, 352, 1262, 849, 1200, 353, 1263
+2749, 849, 1200, 353, 1263, 850, 1201, 354, 1264
+2750, 850, 1201, 354, 1264, 851, 1202, 355, 1265
+2751, 851, 1202, 355, 1265, 852, 1203, 356, 1266
+2752, 852, 1203, 356, 1266, 772, 811, 84, 818
+2753, 206, 246, 37, 248, 862, 1222, 429, 1240
+2754, 862, 1222, 429, 1240, 863, 1223, 430, 1241
+2755, 863, 1223, 430, 1241, 864, 1224, 431, 1242
+2756, 864, 1224, 431, 1242, 865, 1225, 432, 1243
+2757, 865, 1225, 432, 1243, 866, 1226, 433, 1244
+2758, 866, 1226, 433, 1244, 867, 1227, 434, 1245
+2759, 867, 1227, 434, 1245, 868, 1228, 435, 1246
+2760, 868, 1228, 435, 1246, 869, 1229, 436, 1247
+2761, 869, 1229, 436, 1247, 870, 1230, 437, 1248
+2762, 870, 1230, 437, 1248, 774, 814, 93, 816
+2763, 46, 47, 227, 247, 510, 519, 1051, 1231
+2764, 510, 519, 1051, 1231, 511, 520, 1052, 1232
+2765, 511, 520, 1052, 1232, 512, 521, 1053, 1233
+2766, 512, 521, 1053, 1233, 513, 522, 1054, 1234
+2767, 513, 522, 1054, 1234, 514, 523, 1055, 1235
+2768, 514, 523, 1055, 1235, 515, 524, 1056, 1236
+2769, 515, 524, 1056, 1236, 516, 525, 1057, 1237
+2770, 516, 525, 1057, 1237, 517, 526, 1058, 1238
+2771, 517, 526, 1058, 1238, 518, 527, 1059, 1239
+2772, 518, 527, 1059, 1239, 102, 103, 795, 815
+2773, 19, 20, 228, 249, 267, 276, 1060, 1249
+2774, 267, 276, 1060, 1249, 268, 277, 1061, 1250
+2775, 268, 277, 1061, 1250, 269, 278, 1062, 1251
+2776, 269, 278, 1062, 1251, 270, 279, 1063, 1252
+2777, 270, 279, 1063, 1252, 271, 280, 1064, 1253
+2778, 271, 280, 1064, 1253, 272, 281, 1065, 1254
+2779, 272, 281, 1065, 1254, 273, 282, 1066, 1255
+2780, 273, 282, 1066, 1255, 274, 283, 1067, 1256
+2781, 274, 283, 1067, 1256, 275, 284, 1068, 1257
+2782, 275, 284, 1068, 1257, 75, 76, 796, 817
+2783, 28, 29, 229, 250, 348, 357, 1069, 1258
+2784, 348, 357, 1069, 1258, 349, 358, 1070, 1259
+2785, 349, 358, 1070, 1259, 350, 359, 1071, 1260
+2786, 350, 359, 1071, 1260, 351, 360, 1072, 1261
+2787, 351, 360, 1072, 1261, 352, 361, 1073, 1262
+2788, 352, 361, 1073, 1262, 353, 362, 1074, 1263
+2789, 353, 362, 1074, 1263, 354, 363, 1075, 1264
+2790, 354, 363, 1075, 1264, 355, 364, 1076, 1265
+2791, 355, 364, 1076, 1265, 356, 365, 1077, 1266
+2792, 356, 365, 1077, 1266, 84, 85, 797, 818
+2793, 37, 38, 230, 248, 429, 438, 1078, 1240
+2794, 429, 438, 1078, 1240, 430, 439, 1079, 1241
+2795, 430, 439, 1079, 1241, 431, 440, 1080, 1242
+2796, 431, 440, 1080, 1242, 432, 441, 1081, 1243
+2797, 432, 441, 1081, 1243, 433, 442, 1082, 1244
+2798, 433, 442, 1082, 1244, 434, 443, 1083, 1245
+2799, 434, 443, 1083, 1245, 435, 444, 1084, 1246
+2800, 435, 444, 1084, 1246, 436, 445, 1085, 1247
+2801, 436, 445, 1085, 1247, 437, 446, 1086, 1248
+2802, 437, 446, 1086, 1248, 93, 94, 798, 816
+2803, 27, 243, 234, 26, 339, 1195, 1114, 330
+2804, 339, 1195, 1114, 330, 340, 1196, 1115, 331
+2805, 340, 1196, 1115, 331, 341, 1197, 1116, 332
+2806, 341, 1197, 1116, 332, 342, 1198, 1117, 333
+2807, 342, 1198, 1117, 333, 343, 1199, 1118, 334
+2808, 343, 1199, 1118, 334, 344, 1200, 1119, 335
+2809, 344, 1200, 1119, 335, 345, 1201, 1120, 336
+2810, 345, 1201, 1120, 336, 346, 1202, 1121, 337
+2811, 346, 1202, 1121, 337, 347, 1203, 1122, 338
+2812, 347, 1203, 1122, 338, 83, 811, 802, 82
+2813, 45, 244, 232, 44, 501, 1204, 1096, 492
+2814, 501, 1204, 1096, 492, 502, 1205, 1097, 493
+2815, 502, 1205, 1097, 493, 503, 1206, 1098, 494
+2816, 503, 1206, 1098, 494, 504, 1207, 1099, 495
+2817, 504, 1207, 1099, 495, 505, 1208, 1100, 496
+2818, 505, 1208, 1100, 496, 506, 1209, 1101, 497
+2819, 506, 1209, 1101, 497, 507, 1210, 1102, 498
+2820, 507, 1210, 1102, 498, 508, 1211, 1103, 499
+2821, 508, 1211, 1103, 499, 509, 1212, 1104, 500
+2822, 509, 1212, 1104, 500, 101, 812, 800, 100
+2823, 54, 245, 231, 53, 582, 1213, 1087, 573
+2824, 582, 1213, 1087, 573, 583, 1214, 1088, 574
+2825, 583, 1214, 1088, 574, 584, 1215, 1089, 575
+2826, 584, 1215, 1089, 575, 585, 1216, 1090, 576
+2827, 585, 1216, 1090, 576, 586, 1217, 1091, 577
+2828, 586, 1217, 1091, 577, 587, 1218, 1092, 578
+2829, 587, 1218, 1092, 578, 588, 1219, 1093, 579
+2830, 588, 1219, 1093, 579, 589, 1220, 1094, 580
+2831, 589, 1220, 1094, 580, 590, 1221, 1095, 581
+2832, 590, 1221, 1095, 581, 110, 813, 799, 109
+2833, 36, 246, 233, 35, 420, 1222, 1105, 411
+2834, 420, 1222, 1105, 411, 421, 1223, 1106, 412
+2835, 421, 1223, 1106, 412, 422, 1224, 1107, 413
+2836, 422, 1224, 1107, 413, 423, 1225, 1108, 414
+2837, 423, 1225, 1108, 414, 424, 1226, 1109, 415
+2838, 424, 1226, 1109, 415, 425, 1227, 1110, 416
+2839, 425, 1227, 1110, 416, 426, 1228, 1111, 417
+2840, 426, 1228, 1111, 417, 427, 1229, 1112, 418
+2841, 427, 1229, 1112, 418, 428, 1230, 1113, 419
+2842, 428, 1230, 1113, 419, 92, 814, 801, 91
+2843, 217, 64, 63, 239, 961, 717, 708, 1159
+2844, 961, 717, 708, 1159, 962, 718, 709, 1160
+2845, 962, 718, 709, 1160, 963, 719, 710, 1161
+2846, 963, 719, 710, 1161, 964, 720, 711, 1162
+2847, 964, 720, 711, 1162, 965, 721, 712, 1163
+2848, 965, 721, 712, 1163, 966, 722, 713, 1164
+2849, 966, 722, 713, 1164, 967, 723, 714, 1165
+2850, 967, 723, 714, 1165, 968, 724, 715, 1166
+2851, 968, 724, 715, 1166, 969, 725, 716, 1167
+2852, 969, 725, 716, 1167, 785, 121, 122, 807
+2853, 215, 69, 68, 240, 943, 672, 663, 1168
+2854, 943, 672, 663, 1168, 944, 673, 664, 1169
+2855, 944, 673, 664, 1169, 945, 674, 665, 1170
+2856, 945, 674, 665, 1170, 946, 675, 666, 1171
+2857, 946, 675, 666, 1171, 947, 676, 667, 1172
+2858, 947, 676, 667, 1172, 948, 677, 668, 1173
+2859, 948, 677, 668, 1173, 949, 678, 669, 1174
+2860, 949, 678, 669, 1174, 950, 679, 670, 1175
+2861, 950, 679, 670, 1175, 951, 680, 671, 1176
+2862, 951, 680, 671, 1176, 783, 116, 117, 808
+2863, 216, 74, 73, 241, 952, 627, 618, 1177
+2864, 952, 627, 618, 1177, 953, 628, 619, 1178
+2865, 953, 628, 619, 1178, 954, 629, 620, 1179
+2866, 954, 629, 620, 1179, 955, 630, 621, 1180
+2867, 955, 630, 621, 1180, 956, 631, 622, 1181
+2868, 956, 631, 622, 1181, 957, 632, 623, 1182
+2869, 957, 632, 623, 1182, 958, 633, 624, 1183
+2870, 958, 633, 624, 1183, 959, 634, 625, 1184
+2871, 959, 634, 625, 1184, 960, 635, 626, 1185
+2872, 960, 635, 626, 1185, 784, 111, 112, 809
+2873, 218, 59, 58, 242, 970, 762, 753, 1186
+2874, 970, 762, 753, 1186, 971, 763, 754, 1187
+2875, 971, 763, 754, 1187, 972, 764, 755, 1188
+2876, 972, 764, 755, 1188, 973, 765, 756, 1189
+2877, 973, 765, 756, 1189, 974, 766, 757, 1190
+2878, 974, 766, 757, 1190, 975, 767, 758, 1191
+2879, 975, 767, 758, 1191, 976, 768, 759, 1192
+2880, 976, 768, 759, 1192, 977, 769, 760, 1193
+2881, 977, 769, 760, 1193, 978, 770, 761, 1194
+2882, 978, 770, 761, 1194, 786, 126, 127, 810
+2883, 220, 238, 61, 60, 988, 1150, 690, 681
+2884, 988, 1150, 690, 681, 989, 1151, 691, 682
+2885, 989, 1151, 691, 682, 990, 1152, 692, 683
+2886, 990, 1152, 692, 683, 991, 1153, 693, 684
+2887, 991, 1153, 693, 684, 992, 1154, 694, 685
+2888, 992, 1154, 694, 685, 993, 1155, 695, 686
+2889, 993, 1155, 695, 686, 994, 1156, 696, 687
+2890, 994, 1156, 696, 687, 995, 1157, 697, 688
+2891, 995, 1157, 697, 688, 996, 1158, 698, 689
+2892, 996, 1158, 698, 689, 788, 806, 124, 125
+2893, 221, 235, 56, 55, 997, 1123, 735, 726
+2894, 997, 1123, 735, 726, 998, 1124, 736, 727
+2895, 998, 1124, 736, 727, 999, 1125, 737, 728
+2896, 999, 1125, 737, 728, 1000, 1126, 738, 729
+2897, 1000, 1126, 738, 729, 1001, 1127, 739, 730
+2898, 1001, 1127, 739, 730, 1002, 1128, 740, 731
+2899, 1002, 1128, 740, 731, 1003, 1129, 741, 732
+2900, 1003, 1129, 741, 732, 1004, 1130, 742, 733
+2901, 1004, 1130, 742, 733, 1005, 1131, 743, 734
+2902, 1005, 1131, 743, 734, 789, 803, 129, 130
+2903, 222, 237, 66, 65, 1006, 1141, 645, 636
+2904, 1006, 1141, 645, 636, 1007, 1142, 646, 637
+2905, 1007, 1142, 646, 637, 1008, 1143, 647, 638
+2906, 1008, 1143, 647, 638, 1009, 1144, 648, 639
+2907, 1009, 1144, 648, 639, 1010, 1145, 649, 640
+2908, 1010, 1145, 649, 640, 1011, 1146, 650, 641
+2909, 1011, 1146, 650, 641, 1012, 1147, 651, 642
+2910, 1012, 1147, 651, 642, 1013, 1148, 652, 643
+2911, 1013, 1148, 652, 643, 1014, 1149, 653, 644
+2912, 1014, 1149, 653, 644, 790, 805, 119, 120
+2913, 219, 236, 71, 70, 979, 1132, 600, 591
+2914, 979, 1132, 600, 591, 980, 1133, 601, 592
+2915, 980, 1133, 601, 592, 981, 1134, 602, 593
+2916, 981, 1134, 602, 593, 982, 1135, 603, 594
+2917, 982, 1135, 603, 594, 983, 1136, 604, 595
+2918, 983, 1136, 604, 595, 984, 1137, 605, 596
+2919, 984, 1137, 605, 596, 985, 1138, 606, 597
+2920, 985, 1138, 606, 597, 986, 1139, 607, 598
+2921, 986, 1139, 607, 598, 987, 1140, 608, 599
+2922, 987, 1140, 608, 599, 787, 804, 114, 115
+2923, 257, 259, 266, 218, 1321, 1339, 1402, 970
+2924, 1321, 1339, 1402, 970, 1322, 1340, 1403, 971
+2925, 1322, 1340, 1403, 971, 1323, 1341, 1404, 972
+2926, 1323, 1341, 1404, 972, 1324, 1342, 1405, 973
+2927, 1324, 1342, 1405, 973, 1325, 1343, 1406, 974
+2928, 1325, 1343, 1406, 974, 1326, 1344, 1407, 975
+2929, 1326, 1344, 1407, 975, 1327, 1345, 1408, 976
+2930, 1327, 1345, 1408, 976, 1328, 1346, 1409, 977
+2931, 1328, 1346, 1409, 977, 1329, 1347, 1410, 978
+2932, 1329, 1347, 1410, 978, 825, 827, 834, 786
+2933, 255, 261, 264, 215, 1303, 1357, 1384, 943
+2934, 1303, 1357, 1384, 943, 1304, 1358, 1385, 944
+2935, 1304, 1358, 1385, 944, 1305, 1359, 1386, 945
+2936, 1305, 1359, 1386, 945, 1306, 1360, 1387, 946
+2937, 1306, 1360, 1387, 946, 1307, 1361, 1388, 947
+2938, 1307, 1361, 1388, 947, 1308, 1362, 1389, 948
+2939, 1308, 1362, 1389, 948, 1309, 1363, 1390, 949
+2940, 1309, 1363, 1390, 949, 1310, 1364, 1391, 950
+2941, 1310, 1364, 1391, 950, 1311, 1365, 1392, 951
+2942, 1311, 1365, 1392, 951, 823, 829, 832, 783
+2943, 258, 262, 265, 217, 1330, 1366, 1393, 961
+2944, 1330, 1366, 1393, 961, 1331, 1367, 1394, 962
+2945, 1331, 1367, 1394, 962, 1332, 1368, 1395, 963
+2946, 1332, 1368, 1395, 963, 1333, 1369, 1396, 964
+2947, 1333, 1369, 1396, 964, 1334, 1370, 1397, 965
+2948, 1334, 1370, 1397, 965, 1335, 1371, 1398, 966
+2949, 1335, 1371, 1398, 966, 1336, 1372, 1399, 967
+2950, 1336, 1372, 1399, 967, 1337, 1373, 1400, 968
+2951, 1337, 1373, 1400, 968, 1338, 1374, 1401, 969
+2952, 1338, 1374, 1401, 969, 826, 830, 833, 785
+2953, 256, 260, 263, 216, 1312, 1348, 1375, 952
+2954, 1312, 1348, 1375, 952, 1313, 1349, 1376, 953
+2955, 1313, 1349, 1376, 953, 1314, 1350, 1377, 954
+2956, 1314, 1350, 1377, 954, 1315, 1351, 1378, 955
+2957, 1315, 1351, 1378, 955, 1316, 1352, 1379, 956
+2958, 1316, 1352, 1379, 956, 1317, 1353, 1380, 957
+2959, 1317, 1353, 1380, 957, 1318, 1354, 1381, 958
+2960, 1318, 1354, 1381, 958, 1319, 1355, 1382, 959
+2961, 1319, 1355, 1382, 959, 1320, 1356, 1383, 960
+2962, 1320, 1356, 1383, 960, 824, 828, 831, 784
+2963, 252, 219, 264, 261, 1276, 979, 1384, 1357
+2964, 1276, 979, 1384, 1357, 1277, 980, 1385, 1358
+2965, 1277, 980, 1385, 1358, 1278, 981, 1386, 1359
+2966, 1278, 981, 1386, 1359, 1279, 982, 1387, 1360
+2967, 1279, 982, 1387, 1360, 1280, 983, 1388, 1361
+2968, 1280, 983, 1388, 1361, 1281, 984, 1389, 1362
+2969, 1281, 984, 1389, 1362, 1282, 985, 1390, 1363
+2970, 1282, 985, 1390, 1363, 1283, 986, 1391, 1364
+2971, 1283, 986, 1391, 1364, 1284, 987, 1392, 1365
+2972, 1284, 987, 1392, 1365, 820, 787, 832, 829
+2973, 251, 220, 266, 259, 1267, 988, 1402, 1339
+2974, 1267, 988, 1402, 1339, 1268, 989, 1403, 1340
+2975, 1268, 989, 1403, 1340, 1269, 990, 1404, 1341
+2976, 1269, 990, 1404, 1341, 1270, 991, 1405, 1342
+2977, 1270, 991, 1405, 1342, 1271, 992, 1406, 1343
+2978, 1271, 992, 1406, 1343, 1272, 993, 1407, 1344
+2979, 1272, 993, 1407, 1344, 1273, 994, 1408, 1345
+2980, 1273, 994, 1408, 1345, 1274, 995, 1409, 1346
+2981, 1274, 995, 1409, 1346, 1275, 996, 1410, 1347
+2982, 1275, 996, 1410, 1347, 819, 788, 834, 827
+2983, 254, 221, 263, 260, 1294, 997, 1375, 1348
+2984, 1294, 997, 1375, 1348, 1295, 998, 1376, 1349
+2985, 1295, 998, 1376, 1349, 1296, 999, 1377, 1350
+2986, 1296, 999, 1377, 1350, 1297, 1000, 1378, 1351
+2987, 1297, 1000, 1378, 1351, 1298, 1001, 1379, 1352
+2988, 1298, 1001, 1379, 1352, 1299, 1002, 1380, 1353
+2989, 1299, 1002, 1380, 1353, 1300, 1003, 1381, 1354
+2990, 1300, 1003, 1381, 1354, 1301, 1004, 1382, 1355
+2991, 1301, 1004, 1382, 1355, 1302, 1005, 1383, 1356
+2992, 1302, 1005, 1383, 1356, 822, 789, 831, 828
+2993, 253, 222, 265, 262, 1285, 1006, 1393, 1366
+2994, 1285, 1006, 1393, 1366, 1286, 1007, 1394, 1367
+2995, 1286, 1007, 1394, 1367, 1287, 1008, 1395, 1368
+2996, 1287, 1008, 1395, 1368, 1288, 1009, 1396, 1369
+2997, 1288, 1009, 1396, 1369, 1289, 1010, 1397, 1370
+2998, 1289, 1010, 1397, 1370, 1290, 1011, 1398, 1371
+2999, 1290, 1011, 1398, 1371, 1291, 1012, 1399, 1372
+3000, 1291, 1012, 1399, 1372, 1292, 1013, 1400, 1373
+3001, 1292, 1013, 1400, 1373, 1293, 1014, 1401, 1374
+3002, 1293, 1014, 1401, 1374, 821, 790, 833, 830
diff --git a/example/steps/hw32.h b/example/steps/hw32.h
new file mode 100644
index 0000000..46039ee
--- /dev/null
+++ b/example/steps/hw32.h
@@ -0,0 +1,88 @@
+/*  GIMP header image file format (RGB) */
+
+/** \file hw32.h
+ *
+ * This file containts the image data used in the step1 example.
+ */
+
+#ifdef P4EST_ENABLE_DEBUG
+static unsigned int width = 32;
+static unsigned int height = 32;
+#endif
+
+/*  Call this macro repeatedly.  After each use, the pixel data can be extracted  */
+
+/** Access a pixel of the image and move the data pointer forward. */
+#define HW32_HEADER_PIXEL(data,pixel) {\
+pixel[0] = (((data[0] - 33) << 2) | ((data[1] - 33) >> 4)); \
+pixel[1] = ((((data[1] - 33) & 0xF) << 4) | ((data[2] - 33) >> 2)); \
+pixel[2] = ((((data[2] - 33) & 0x3) << 6) | ((data[3] - 33))); \
+data += 4; \
+}
+/** The image data is encoded as a C string. */
+static const char *hw32_header_data =
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````!!!!````````!!!!````````````````````````````````````"
+	"````````````````!!!!````````````````!!!!````````````````````````"
+	"````````````!!!!````````!!!!````````````````````````````````````"
+	"````````````````!!!!````````````````!!!!````````````````````````"
+	"````````````!!!!````````!!!!````````````````````````````````````"
+	"````````````````!!!!````````````````!!!!````````[OLK45V.$1U.,3UN"
+	"S=H*````````!!!!````````!!!!````````Y/$A0T]`$!Q-1%\"!Y?(B````````"
+	"````````````````!!!!!!!!!!!!!!!!!!!!!!!!````````76F:I+#A_0DYNL;W"
+	",CYO````````!!!!````````!!!!````````45V.?8FZ]@(R>X>X5&\"1````````"
+	"````````````````!!!!````````````````!!!!````````%R-4!!!!!!!!!!!!"
+	"!A)#````````!!!!````````!!!!````````%\"!1\\_`P````\\O\\O%2%2````````"
+	"````````````````!!!!````````````````!!!!````````%B)3Z?8F````````"
+	"````````````!!!!````````!!!!````````%\"!1\\_`P````\\O\\O%2%2````````"
+	"````````````````!!!!````````````````!!!!````````7VN<7VN<[OLKYO,C"
+	"76F:````````!!!!````````!!!!````````45V.@(R]]P,S?8FZ5&\"1````````"
+	"````````````````!!!!````````````````!!!!````````\\?XN7FJ;%2%2)3%B"
+	"KKKK````````!!!!````````!!!!````````Y/$A0T]`#QM,1%\"!Y?(B````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````!!!!````````````````````````!!!!````"
+	"````(R]@W.D9````````.T=X````````W>H:(R]@````````````````````````"
+	"````````````````````````````!!!!````````````````````````!!!!````"
+	"````8FZ?G:G:````X>X>!!!!V>86````HJ[?8V^@````````````````````````"
+	"````````````````````````````!!!!````````````````````````!!!!````"
+	"````HJ[?7FJ;````HZ_@$1U.E*#1````9W.DHJ[?````Y/$A0T]`$!Q-1%\"!Y?(B"
+	"````````!!!!DI[/(\"Q=!1%\"````!!!!````````XN\\?.45V#QM,>X>X!!!!````"
+	"````X>X>'RM<````9'\"ABY?(3UN,````*S=HXN\\?````45V.?8FZ]@(R>X>X5&\"1"
+	"````````!!!!:'2E[OLK````````!!!!````````45V.@(R]]@(R?8FZ!!!!````"
+	"````````(2U>X.T=-T-T_0DY(2U>ZO<G(BY?````````%\"!1\\_`P````\\O\\O%2%2"
+	"````````!!!!\\_`P````````````!!!!````````$Q]0\\_`P````\\_`P!!!!````"
+	"````````8&R=A9'\"6F:7````6666=X.T86V>````````%\"!1\\_`P````\\O\\O%2%2"
+	"````````!!!!````````````````!!!!````````$Q]0\\_`P````\\_`P!!!!````"
+	"````````H*S=%2%2HZ_@````HJ[?#AI+H*S=````````45V.@(R]]P,S?8FZ5&\"1"
+	"````````!!!!````````````````!!!!````````45V.@(R]]P,S?8FZ!!!!````"
+	"````````W^P<!!!!ZO<G````ZO<G!!!!X.T=````````Y/$A0T]`#QM,1%\"!Y?(B"
+	"````````!!!!````````````````!!!!````````XN\\?.45V#QM,>X>X!!!!````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"````````````````````````````````````````````````````````````````"
+	"";
diff --git a/example/steps/p4est_step1.c b/example/steps/p4est_step1.c
new file mode 100644
index 0000000..d91b7e4
--- /dev/null
+++ b/example/steps/p4est_step1.c
@@ -0,0 +1,188 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_step1.c
+ *
+ * This 2D example program refines a domain based on given image data.
+ * The image file hw32.h has been created with the GIMP and is compiled in.
+ */
+
+/* p4est has two separate interfaces for 2D and 3D, p4est*.h and p8est*.h.
+ * Most API functions are available for both dimensions.  The header file
+ * p4est_to_p8est.h #define's the 2D names to the 3D names such that most code
+ * only needs to be written once.  In this example, we rely on this. */
+#ifndef P4_TO_P8
+#include <p4est_vtk.h>
+#else
+#include <p8est_vtk.h>
+#endif
+#include "hw32.h"
+
+/** The resolution of the image data in powers of two. */
+#define P4EST_STEP1_PATTERN_LEVEL 5
+/** The dimension of the image data. */
+#define P4EST_STEP1_PATTERN_LENGTH (1 << P4EST_STEP1_PATTERN_LEVEL)
+static const int    plv = P4EST_STEP1_PATTERN_LEVEL;    /**< Shortcut */
+static const int    ple = P4EST_STEP1_PATTERN_LENGTH;   /**< Shortcut */
+#ifdef P4_TO_P8
+static const p4est_qcoord_t eighth = P4EST_QUADRANT_LEN (3);
+#endif
+
+/** Callback function to decide on refinement.
+ *
+ * Refinement and coarsening is controlled by callback functions.
+ * This function is called for every processor-local quadrant in order; its
+ * return value is understood as a boolean refinement flag.
+ * In this example we use the image file hw32.h to determine the refinement.
+ */
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 tilelen;
+  int                 offsi, offsj;
+  int                 i, j;
+  const char         *d;
+  unsigned char       p[3];
+
+  /* The connectivity chosen in main () only consists of one tree. */
+  P4EST_ASSERT (which_tree == 0);
+
+  /* We do not want to refine deeper than a given maximum level. */
+  if (quadrant->level > plv) {
+    return 0;
+  }
+#ifdef P4_TO_P8
+  /* In 3D we extrude the 2D image in the z direction between [3/8, 5/8]. */
+  if (quadrant->level >= 3 &&
+      (quadrant->z < 3 * eighth || quadrant->z >= 5 * eighth)) {
+    return 0;
+  }
+#endif
+
+  /* We read the image data and refine wherever the color value is dark.
+   * We can then visualize the output and highlight level > PATTERN_LEVEL. */
+  tilelen = 1 << (plv - quadrant->level);       /* Pixel size of quadrant */
+  offsi = quadrant->x / P4EST_QUADRANT_LEN (plv);       /* Pixel x offset */
+  offsj = quadrant->y / P4EST_QUADRANT_LEN (plv);       /* Pixel y offset */
+  P4EST_ASSERT (offsi >= 0 && offsj >= 0);
+  for (j = 0; j < tilelen; ++j) {
+    P4EST_ASSERT (offsj + j < ple);
+    for (i = 0; i < tilelen; ++i) {
+      P4EST_ASSERT (offsi + i < ple);
+      d =
+        hw32_header_data + 4 * (ple * (ple - 1 - (offsj + j)) + (offsi + i));
+      HW32_HEADER_PIXEL (d, p);
+      P4EST_ASSERT (p[0] == p[1] && p[1] == p[2]);      /* Grayscale image */
+      if (p[0] < 128) {
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+/** The main function of the step1 example program.
+ *
+ * It creates a connectivity and forest, refines it, and writes a VTK file.
+ */
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 recursive, partforcoarsen, balance;
+  sc_MPI_Comm         mpicomm;
+  p4est_t            *p4est;
+  p4est_connectivity_t *conn;
+
+  /* Initialize MPI; see sc_mpi.h.
+   * If configure --enable-mpi is given these are true MPI calls.
+   * Else these are dummy functions that simulate a single-processor run. */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+
+  /* These functions are optional.  If called they store the MPI rank as a
+   * static variable so subsequent global p4est log messages are only issued
+   * from processor zero.  Here we turn off most of the logging; see sc.h. */
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_ESSENTIAL);
+  p4est_init (NULL, SC_LP_PRODUCTION);
+  P4EST_GLOBAL_PRODUCTIONF
+    ("This is the p4est %dD demo example/steps/%s_step1\n",
+     P4EST_DIM, P4EST_STRING);
+
+  /* Create a forest that consists of just one quadtree/octree.
+   * This file is compiled for both 2D and 3D: the macro P4_TO_P8 can be
+   * checked to execute dimension-dependent code. */
+#ifndef P4_TO_P8
+  conn = p4est_connectivity_new_unitsquare ();
+#else
+  conn = p8est_connectivity_new_unitcube ();
+#endif
+
+  /* Create a forest that is not refined; it consists of the root octant. */
+  p4est = p4est_new (mpicomm, conn, 0, NULL, NULL);
+
+  /* Refine the forest recursively in parallel.
+   * Since refinement does not change the partition boundary, this call
+   * must not create an overly large number of quadrants.  A numerical
+   * application would call p4est_refine non-recursively in a loop,
+   * repartitioning in each iteration.
+   * The P4EST_ASSERT macro only activates with --enable-debug.
+   * We check against the data dimensions in example/steps/hw32.h. */
+  P4EST_ASSERT (P4EST_STEP1_PATTERN_LENGTH == width);
+  P4EST_ASSERT (P4EST_STEP1_PATTERN_LENGTH == height);
+  recursive = 1;
+  p4est_refine (p4est, recursive, refine_fn, NULL);
+
+  /* Partition: The quadrants are redistributed for equal element count.  The
+   * partition can optionally be modified such that a family of octants, which
+   * are possibly ready for coarsening, are never split between processors. */
+  partforcoarsen = 0;
+  p4est_partition (p4est, partforcoarsen, NULL);
+
+  /* If we call the 2:1 balance we ensure that neighbors do not differ in size
+   * by more than a factor of 2.  This can optionally include diagonal
+   * neighbors across edges or corners as well; see p4est.h. */
+  balance = 1;
+  if (balance) {
+    p4est_balance (p4est, P4EST_CONNECT_FACE, NULL);
+    p4est_partition (p4est, partforcoarsen, NULL);
+  }
+
+  /* Write the forest to disk for visualization, one file per processor. */
+  p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_step1");
+
+  /* Destroy the p4est and the connectivity structure. */
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (conn);
+
+  /* Verify that allocations internal to p4est and sc do not leak memory.
+   * This should be called if sc_init () has been called earlier. */
+  sc_finalize ();
+
+  /* This is standard MPI programs.  Without --enable-mpi, this is a dummy. */
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+  return 0;
+}
diff --git a/example/steps/p4est_step2.c b/example/steps/p4est_step2.c
new file mode 100644
index 0000000..e376d35
--- /dev/null
+++ b/example/steps/p4est_step2.c
@@ -0,0 +1,159 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_step2.c
+ *
+ * This 2D example program refines a domain given by an ABAQUS .inp file.
+ */
+
+/* p4est has two separate interfaces for 2D and 3D, p4est*.h and p8est*.h.
+ * Most API functions are available for both dimensions.  The header file
+ * p4est_to_p8est.h #define's the 2D names to the 3D names such that most code
+ * only needs to be written once.  In this example, we rely on this
+ * to compile both 2D and 3D from the same source file. */
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_vtk.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_vtk.h>
+#endif
+
+/** We're not using p4est->user_pointer here but take a shortcut.
+ */
+static int          refine_level = 0;
+
+/** Callback function to decide on refinement.
+ *
+ * Refinement and coarsening is controlled by callback functions.
+ * This function is called for every processor-local quadrant in order; its
+ * return value is understood as a boolean refinement flag.
+ *
+ * Here we use uniform refinement.  Note that this function is not suitable for
+ * recursive refinement and must be used in an iterative fashion.
+ */
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  return 1;
+}
+
+/** The main function of the step2 example program.
+ *
+ * It creates a connectivity from an ABAQUS .inp file and forest, refines it,
+ * and writes a VTK file.
+ */
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 balance;
+  int                 level;
+  sc_MPI_Comm         mpicomm;
+  p4est_t            *p4est;
+  p4est_connectivity_t *conn;
+  const char         *filename;
+
+  /* Initialize MPI; see sc_mpi.h.
+   * If configure --enable-mpi is given these are true MPI calls.
+   * Else these are dummy functions that simulate a single-processor run. */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+
+  /* These functions are optional.  If called they store the MPI rank as a
+   * static variable so subsequent global p4est log messages are only issued
+   * from processor zero.  Here we turn off most of the logging; see sc.h. */
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_ESSENTIAL);
+  p4est_init (NULL, SC_LP_PRODUCTION);
+  P4EST_GLOBAL_PRODUCTIONF
+    ("This is the p4est %dD demo example/steps/%s_step2\n",
+     P4EST_DIM, P4EST_STRING);
+
+  /* Get the .inp file name from the list of arguments along with an optional
+   * level of refinement.  */
+  if (argc != 2 && argc != 3) {
+    SC_GLOBAL_LERRORF ("Usage: %s <inp file name> [level of refinement]\n",
+                       argv[0]);
+    sc_abort ();
+  }
+  filename = argv[1];
+  if (argc == 3)
+    refine_level = atoi (argv[2]);
+
+  /* Create a forest from the inp file with name filename  */
+  conn = p4est_connectivity_read_inp (filename);
+  if (conn == NULL) {
+    P4EST_LERRORF ("Failed to read a valid connectivity from %s\n", filename);
+    sc_abort ();
+  }
+
+#ifdef P4EST_WITH_METIS
+  /* Use metis (if p4est is compiled with the flag '--with-metis') to
+   * reorder the connectivity for better partitioning of the forest
+   * across processors.
+   */
+  p4est_connectivity_reorder (mpicomm, 0, conn, P4EST_CONNECT_FACE);
+#endif /* P4EST_WITH_METIS */
+
+  /* Create a forest that is not refined; it consists of the root octant. */
+  p4est = p4est_new (mpicomm, conn, 0, NULL, NULL);
+
+  /* Refine the forest iteratively, load balancing at each iteration.
+   * This is important when starting with an unrefined forest */
+  for (level = 0; level < refine_level; ++level) {
+    p4est_refine (p4est, 0, refine_fn, NULL);
+    /* Refinement has lead to up to 8x more elements; redistribute them. */
+    p4est_partition (p4est, 0, NULL);
+  }
+
+  /* If we call the 2:1 balance we ensure that neighbors do not differ in size
+   * by more than a factor of 2.  This can optionally include diagonal
+   * neighbors across edges or corners as well; see p4est.h.
+   *
+   * Note that this balance step is not strictly necessary since we are using
+   * uniform refinement but may be required for other types of refinement.
+   */
+  balance = 1;
+  if (balance) {
+    p4est_balance (p4est, P4EST_CONNECT_FACE, NULL);
+    p4est_partition (p4est, 0, NULL);
+  }
+
+  /* Write the forest to disk for visualization, one file per processor. */
+  p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_step2");
+
+  /* Destroy the p4est and the connectivity structure. */
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (conn);
+
+  /* Verify that allocations internal to p4est and sc do not leak memory.
+   * This should be called if sc_init () has been called earlier. */
+  sc_finalize ();
+
+  /* This is standard MPI programs.  Without --enable-mpi, this is a dummy. */
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+  return 0;
+}
diff --git a/example/steps/p4est_step3.c b/example/steps/p4est_step3.c
new file mode 100644
index 0000000..20c82ef
--- /dev/null
+++ b/example/steps/p4est_step3.c
@@ -0,0 +1,1219 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_step3.c
+ *
+ * This 2D example program uses p4est to solve a simple advection problem.  It
+ * is numerically very simple, and intended to demonstrate several methods of
+ * interacting with the p4est data after it has been refined and partitioned.
+ * It demonstrates the construction of ghost layers (see p4est_ghost_t in
+ * p4est_ghost.h) and communication of ghost-layer data, and it demonstrates
+ * interacting with the quadrants and quadrant boundaries through the
+ * p4est_iterate() routine (see p4est_iterate.h).
+ */
+
+/* p4est has two separate interfaces for 2D and 3D, p4est*.h and p8est*.h.
+ * Most API functions are available for both dimensions.  The header file
+ * p4est_to_p8est.h #define's the 2D names to the 3D names such that most code
+ * only needs to be written once.  In this example, we rely on this. */
+#ifndef P4_TO_P8
+#include <p4est_vtk.h>
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_iterate.h>
+#else
+#include <p8est_vtk.h>
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_iterate.h>
+#endif
+
+/* In this example we store data with each quadrant/octant. */
+
+/** Per-quadrant data for this example.
+ *
+ * In this problem, we keep track of the state variable u, its
+ * derivatives in space, and in time.
+ */
+typedef struct step3_data
+{
+  double              u;             /**< the state variable */
+  double              du[P4EST_DIM]; /**< the spatial derivatives */
+  double              dudt;          /**< the time derivative */
+}
+step3_data_t;
+
+/** The example parameters.
+ *
+ * This describes the advection problem and time-stepping used in this
+ * example.
+ */
+typedef struct step3_ctx
+{
+  double              center[P4EST_DIM];  /**< coordinates of the center of
+                                               the initial condition Gaussian
+                                               bump */
+  double              bump_width;         /**< width of the initial condition
+                                               Gaussian bump */
+  double              max_err;            /**< maximum allowed global
+                                               interpolation error */
+  double              v[P4EST_DIM];       /**< the advection velocity */
+  int                 refine_period;      /**< the number of time steps
+                                               between mesh refinement */
+  int                 repartition_period; /**< the number of time steps
+                                               between repartitioning */
+  int                 write_period;       /**< the number of time steps
+                                               between writing vtk files */
+}
+step3_ctx_t;
+
+/** Compute the value and derivatives of the initial condition.
+ *
+ * \param [in]  x   the coordinates
+ * \param [out] du  the derivative at \a x
+ * \param [in]  ctx the example parameters
+ *
+ * \return the initial condition at \a x
+ */
+static double
+step3_initial_condition (double x[], double du[], step3_ctx_t * ctx)
+{
+  int                 i;
+  double             *c = ctx->center;
+  double              bump_width = ctx->bump_width;
+  double              r2, d[P4EST_DIM];
+  double              arg, retval;
+
+  r2 = 0.;
+  for (i = 0; i < P4EST_DIM; i++) {
+    d[i] = x[i] - c[i];
+    r2 += d[i] * d[i];
+  }
+
+  arg = -(1. / 2.) * r2 / bump_width / bump_width;
+  retval = exp (arg);
+
+  if (du) {
+    for (i = 0; i < P4EST_DIM; i++) {
+      du[i] = -(1. / bump_width / bump_width) * d[i] * retval;
+    }
+  }
+
+  return retval;
+}
+
+/** Get the coordinates of the midpoint of a quadrant.
+ *
+ * \param [in]  p4est      the forest
+ * \param [in]  which_tree the tree in the forest containing \a q
+ * \param [in]  q          the quadrant
+ * \param [out] xyz        the coordinates of the midpoint of \a q
+ */
+static void
+step3_get_midpoint (p4est_t * p4est, p4est_topidx_t which_tree,
+                    p4est_quadrant_t * q, double xyz[3])
+{
+  p4est_qcoord_t      half_length = P4EST_QUADRANT_LEN (q->level) / 2;
+
+  p4est_qcoord_to_vertex (p4est->connectivity, which_tree,
+                          q->x + half_length, q->y + half_length,
+#ifdef P4_TO_P8
+                          q->z + half_length,
+#endif
+                          xyz);
+}
+
+/** Initialize the initial condition data of a quadrant.
+ *
+ * This function matches the p4est_init_t prototype that is used by
+ * p4est_new(), p4est_refine(), p4est_coarsen(), and p4est_balance().
+ *
+ * \param [in] p4est          the forest
+ * \param [in] which_tree     the tree in the forest containing \a q
+ * \param [in,out] q          the quadrant whose data gets initialized
+ */
+static void
+step3_init_initial_condition (p4est_t * p4est, p4est_topidx_t which_tree,
+                              p4est_quadrant_t * q)
+{
+  /* the data associated with a forest is accessible by user_pointer */
+  step3_ctx_t        *ctx = (step3_ctx_t *) p4est->user_pointer;
+  /* the data associated with a quadrant is accessible by p.user_data */
+  step3_data_t       *data = (step3_data_t *) q->p.user_data;
+  double              midpoint[3];
+
+  step3_get_midpoint (p4est, which_tree, q, midpoint);
+  /* initialize the data */
+  data->u = step3_initial_condition (midpoint, data->du, ctx);
+}
+
+/** Estimate the square of the approximation error on a quadrant.
+ *
+ * We compute our estimate by integrating the difference of a constant
+ * approximation at the midpoint and a linear approximation that interpolates
+ * at the midpoint.
+ *
+ * \param [in] q a quadrant
+ *
+ * \return the square of the error estimate for the state variables contained
+ * in \a q's data.
+ */
+static double
+step3_error_sqr_estimate (p4est_quadrant_t * q)
+{
+  step3_data_t       *data = (step3_data_t *) q->p.user_data;
+  int                 i;
+  double              diff2;
+  double             *du = data->du;
+  double              h =
+    (double) P4EST_QUADRANT_LEN (q->level) / (double) P4EST_ROOT_LEN;
+  double              vol;
+
+#ifdef P4_TO_P8
+  vol = h * h * h;
+#else
+  vol = h * h;
+#endif
+
+  diff2 = 0.;
+  /* use the approximate derivative to estimate the L2 error */
+  for (i = 0; i < P4EST_DIM; i++) {
+    diff2 += du[i] * du[i] * (1. / 12.) * h * h * vol;
+  }
+
+  return diff2;
+}
+
+/** Refine by the L2 error estimate.
+ *
+ * Given the maximum global error, we enforce that each quadrant's portion of
+ * the error must not exceed is fraction of the total volume of the domain
+ * (which is 1).
+ *
+ * This function matches the p4est_refine_t prototype that is used by
+ * p4est_refine() and p4est_refine_ext().
+ *
+ * \param [in] p4est          the forest
+ * \param [in] which_tree     the tree in the forest containing \a q
+ * \param [in] q              the quadrant
+ *
+ * \return 1 if \a q should be refined, 0 otherwise.
+ */
+static int
+step3_refine_err_estimate (p4est_t * p4est, p4est_topidx_t which_tree,
+                           p4est_quadrant_t * q)
+{
+  step3_ctx_t        *ctx = (step3_ctx_t *) p4est->user_pointer;
+  double              global_err = ctx->max_err;
+  double              global_err2 = global_err * global_err;
+  double              h =
+    (double) P4EST_QUADRANT_LEN (q->level) / (double) P4EST_ROOT_LEN;
+  double              vol, err2;
+
+  /* the quadrant's volume is also its volume fraction */
+#ifdef P4_TO_P8
+  vol = h * h * h;
+#else
+  vol = h * h;
+#endif
+
+  err2 = step3_error_sqr_estimate (q);
+  if (err2 > global_err2 * vol) {
+    return 1;
+  }
+  else {
+    return 0;
+  }
+}
+
+/** Coarsen by the L2 error estimate of the initial condition.
+ *
+ * Given the maximum global error, we enforce that each quadrant's portion of
+ * the error must not exceed is fraction of the total volume of the domain
+ * (which is 1).
+ *
+ * \param [in] p4est          the forest
+ * \param [in] which_tree     the tree in the forest containing \a children
+ * \param [in] children       a family of quadrants
+ *
+ * \return 1 if \a children should be coarsened, 0 otherwise.
+ */
+static int
+step3_coarsen_initial_condition (p4est_t * p4est,
+                                 p4est_topidx_t which_tree,
+                                 p4est_quadrant_t * children[])
+{
+  p4est_quadrant_t    parent;
+  step3_ctx_t        *ctx = (step3_ctx_t *) p4est->user_pointer;
+  double              global_err = ctx->max_err;
+  double              global_err2 = global_err * global_err;
+  double              h;
+  step3_data_t        parentdata;
+  double              parentmidpoint[3];
+  double              vol, err2;
+
+  /* get the parent of the first child (the parent of all children) */
+  p4est_quadrant_parent (children[0], &parent);
+  step3_get_midpoint (p4est, which_tree, &parent, parentmidpoint);
+  parentdata.u = step3_initial_condition (parentmidpoint, parentdata.du, ctx);
+  h = (double) P4EST_QUADRANT_LEN (parent.level) / (double) P4EST_ROOT_LEN;
+  /* the quadrant's volume is also its volume fraction */
+#ifdef P4_TO_P8
+  vol = h * h * h;
+#else
+  vol = h * h;
+#endif
+  parent.p.user_data = (void *) (&parentdata);
+
+  err2 = step3_error_sqr_estimate (&parent);
+  if (err2 < global_err2 * vol) {
+    return 1;
+  }
+  else {
+    return 0;
+  }
+}
+
+/** Coarsen by the L2 error estimate of the current state approximation.
+ *
+ * Given the maximum global error, we enforce that each quadrant's portion of
+ * the error must not exceed its fraction of the total volume of the domain
+ * (which is 1).
+ *
+ * This function matches the p4est_coarsen_t prototype that is used by
+ * p4est_coarsen() and p4est_coarsen_ext().
+ *
+ * \param [in] p4est          the forest
+ * \param [in] which_tree     the tree in the forest containing \a children
+ * \param [in] children       a family of quadrants
+ *
+ * \return 1 if \a children should be coarsened, 0 otherwise.
+ */
+static int
+step3_coarsen_err_estimate (p4est_t * p4est,
+                            p4est_topidx_t which_tree,
+                            p4est_quadrant_t * children[])
+{
+  step3_ctx_t        *ctx = (step3_ctx_t *) p4est->user_pointer;
+  double              global_err = ctx->max_err;
+  double              global_err2 = global_err * global_err;
+  double              h;
+  step3_data_t       *data;
+  double              vol, err2, childerr2;
+  double              parentu;
+  double              diff;
+  int                 i;
+
+  h =
+    (double) P4EST_QUADRANT_LEN (children[0]->level) /
+    (double) P4EST_ROOT_LEN;
+  /* the quadrant's volume is also its volume fraction */
+#ifdef P4_TO_P8
+  vol = h * h * h;
+#else
+  vol = h * h;
+#endif
+
+  /* compute the average */
+  parentu = 0.;
+  for (i = 0; i < P4EST_CHILDREN; i++) {
+    data = (step3_data_t *) children[i]->p.user_data;
+    parentu += data->u / P4EST_CHILDREN;
+  }
+
+  err2 = 0.;
+  for (i = 0; i < P4EST_CHILDREN; i++) {
+    childerr2 = step3_error_sqr_estimate (children[i]);
+
+    if (childerr2 > global_err2 * vol) {
+      return 0;
+    }
+    err2 += step3_error_sqr_estimate (children[i]);
+    diff = (parentu - data->u) * (parentu - data->u);
+    err2 += diff * vol;
+  }
+  if (err2 < global_err2 * (vol * P4EST_CHILDREN)) {
+    return 1;
+  }
+  else {
+    return 0;
+  }
+}
+
+/** Initialize the state variables of incoming quadrants from outgoing
+ * quadrants.
+ *
+ * The functions p4est_refine_ext(), p4est_coarsen_ext(), and
+ * p4est_balance_ext() take as an argument a p4est_replace_t callback function,
+ * which allows one to setup the quadrant data of incoming quadrants from the
+ * data of outgoing quadrants, before the outgoing data is destroyed.  This
+ * function matches the p4est_replace_t prototype.
+ *
+ * In this example, we linearly interpolate the state variable of a quadrant
+ * that is refined to its children, and we average the midpoints of children
+ * that are being coarsened to the parent.
+ *
+ * \param [in] p4est          the forest
+ * \param [in] which_tree     the tree in the forest containing \a children
+ * \param [in] num_outgoing   the number of quadrants that are being replaced:
+ *                            either 1 if a quadrant is being refined, or
+ *                            P4EST_CHILDREN if a family of children are being
+ *                            coarsened.
+ * \param [in] outgoing       the outgoing quadrants
+ * \param [in] num_incoming   the number of quadrants that are being added:
+ *                            either P4EST_CHILDREN if a quadrant is being refined, or
+ *                            1 if a family of children are being
+ *                            coarsened.
+ * \param [in,out] incoming   quadrants whose data are initialized.
+ */
+static void
+step3_replace_quads (p4est_t * p4est, p4est_topidx_t which_tree,
+                     int num_outgoing,
+                     p4est_quadrant_t * outgoing[],
+                     int num_incoming, p4est_quadrant_t * incoming[])
+{
+  step3_data_t       *parent_data, *child_data;
+  int                 i, j;
+  double              h;
+  double              du_old, du_est;
+
+  if (num_outgoing > 1) {
+    /* this is coarsening */
+    parent_data = (step3_data_t *) incoming[0]->p.user_data;
+    parent_data->u = 0.;
+    for (j = 0; j < P4EST_DIM; j++) {
+      parent_data->du[j] = (1. / 0.);
+
+    }
+    for (i = 0; i < P4EST_CHILDREN; i++) {
+      child_data = (step3_data_t *) outgoing[i]->p.user_data;
+      parent_data->u += child_data->u / P4EST_CHILDREN;
+      for (j = 0; j < P4EST_DIM; j++) {
+        du_old = parent_data->du[j];
+        du_est = child_data->du[j];
+
+        if (du_old == du_old) {
+          if (du_est * du_old >= 0.) {
+            if (fabs (du_est) < fabs (du_old)) {
+              parent_data->du[j] = du_est;
+            }
+          }
+          else {
+            parent_data->du[j] = 0.;
+          }
+        }
+        else {
+          parent_data->du[j] = du_est;
+        }
+      }
+    }
+  }
+  else {
+    /* this is refinement */
+    parent_data = (step3_data_t *) outgoing[0]->p.user_data;
+    h =
+      (double) P4EST_QUADRANT_LEN (outgoing[0]->level) /
+      (double) P4EST_ROOT_LEN;
+
+    for (i = 0; i < P4EST_CHILDREN; i++) {
+      child_data = (step3_data_t *) incoming[i]->p.user_data;
+      child_data->u = parent_data->u;
+      for (j = 0; j < P4EST_DIM; j++) {
+        child_data->du[j] = parent_data->du[j];
+        child_data->u +=
+          (h / 4.) * parent_data->du[j] * ((i & (1 << j)) ? 1. : -1);
+      }
+    }
+  }
+}
+
+/** Callback function for interpolating the solution from quadrant midpoints to
+ * corners.
+ *
+ * The function p4est_iterate() takes as an argument a p4est_iter_volume_t
+ * callback function, which it executes at every local quadrant (see
+ * p4est_iterate.h).  This function matches the p4est_iter_volume_t prototype.
+ *
+ * In this example, we use the callback function to interpolate the state
+ * variable to the corners, and write those corners into an array so that they
+ * can be written out.
+ *
+ * \param [in] info          the information about this quadrant that has been
+ *                           populated by p4est_iterate()
+ * \param [in,out] user_data the user_data that was given as an argument to
+ *                           p4est_iterate: in this case, it points to the
+ *                           array of corner values that we want to write.
+ *                           The values for the corner of the quadrant
+ *                           described by \a info are written during the
+ *                           execution of the callback.
+ */
+static void
+step3_interpolate_solution (p4est_iter_volume_info_t * info, void *user_data)
+{
+  double             *u_interp = (double *) user_data;  /* we passed the array of values to fill as the user_data in the call to p4est_iterate */
+  p4est_t            *p4est = info->p4est;
+  p4est_quadrant_t   *q = info->quad;
+  p4est_topidx_t      which_tree = info->treeid;
+  p4est_locidx_t      local_id = info->quadid;  /* this is the index of q *within its tree's numbering*.  We want to convert it its index for all the quadrants on this process, which we do below */
+  p4est_tree_t       *tree;
+  step3_data_t       *data = (step3_data_t *) q->p.user_data;
+  double              h;
+  p4est_locidx_t      arrayoffset;
+  double              this_u;
+  int                 i, j;
+
+  tree = p4est_tree_array_index (p4est->trees, which_tree);
+  local_id += tree->quadrants_offset;   /* now the id is relative to the MPI process */
+  arrayoffset = P4EST_CHILDREN * local_id;      /* each local quadrant has 2^d (P4EST_CHILDREN) values in u_interp */
+  h = (double) P4EST_QUADRANT_LEN (q->level) / (double) P4EST_ROOT_LEN;
+
+  for (i = 0; i < P4EST_CHILDREN; i++) {
+    this_u = data->u;
+    /* loop over the derivative components and linearly interpolate from the
+     * midpoint to the corners */
+    for (j = 0; j < P4EST_DIM; j++) {
+      /* In order to know whether the direction from the midpoint to the corner is
+       * negative or positive, we take advantage of the fact that the corners
+       * are in z-order.  If i is an odd number, it is on the +x side; if it
+       * is even, it is on the -x side.  If (i / 2) is an odd number, it is on
+       * the +y side, etc. */
+      this_u += (h / 2) * data->du[j] * ((i & (1 << j)) ? 1. : -1.);
+    }
+    u_interp[arrayoffset + i] = this_u;
+  }
+
+}
+
+/** Write the state variable to vtk format, one file per process.
+ *
+ * \param [in] p4est    the forest, whose quadrant data contains the state
+ * \param [in] timestep the timestep number, used to name the output files
+ */
+static void
+step3_write_solution (p4est_t * p4est, int timestep)
+{
+  char                filename[BUFSIZ] = { '\0' };
+  double             *u_interp;
+  p4est_locidx_t      numquads;
+
+  snprintf (filename, 17, P4EST_STRING "_step3_%04d", timestep);
+
+  numquads = p4est->local_num_quadrants;
+
+  /* create a vector with one value for the corner of every local quadrant
+   * (the number of children is always the same as the number of corners) */
+  u_interp = P4EST_ALLOC (double, numquads * P4EST_CHILDREN);
+
+  /* Use the iterator to visit every cell and fill in the solution values.
+   * Using the iterator is not absolutely necessary in this case: we could
+   * also loop over every tree (there is only one tree in this case) and loop
+   * over every quadrant within every tree, but we are trying to demonstrate
+   * the usage of p4est_iterate in this example */
+  p4est_iterate (p4est, NULL,   /* we don't need any ghost quadrants for this loop */
+                 (void *) u_interp,     /* pass in u_interp so that we can fill it */
+                 step3_interpolate_solution,    /* callback function that interpolate from the cell center to the cell corners, defined above */
+                 NULL,          /* there is no callback for the faces between quadrants */
+#ifdef P4_TO_P8
+                 NULL,          /* there is no callback for the edges between quadrants */
+#endif
+                 NULL);         /* there is no callback for the corners between quadrants */
+
+  p4est_vtk_write_all (p4est, NULL,     /* we do not need to transform from the vertex space into physical space, so we do not need a p4est_geometry_t * pointer */
+                       0.99,    /* draw each quadrant at almost full scale */
+                       0,       /* do not write the tree id's of each quadrant (there is only one tree in this example) */
+                       1,       /* do write the refinement level of each quadrant */
+                       1,       /* do write the mpi process id of each quadrant */
+                       0,       /* do not wrap the mpi rank (if this were > 0, the modulus of the rank relative to this number would be written instead of the rank) */
+                       1,       /* write one scalar field: the solution value */
+                       0,       /* write no vector fields */
+                       filename, "solution", u_interp);
+
+  P4EST_FREE (u_interp);
+}
+
+/** Approximate the divergence of (vu) on each quadrant
+ *
+ * We use piecewise constant approximations on each quadrant, so the value is
+ * always 0.
+ *
+ * Like step3_interpolate_solution(), this function matches the
+ * p4est_iter_volume_t prototype used by p4est_iterate().
+ *
+ * \param [in] info          the information about the quadrant populated by
+ *                           p4est_iterate()
+ * \param [in] user_data     not used
+ */
+static void
+step3_quad_divergence (p4est_iter_volume_info_t * info, void *user_data)
+{
+  p4est_quadrant_t   *q = info->quad;
+  step3_data_t       *data = (step3_data_t *) q->p.user_data;
+
+  data->dudt = 0.;
+}
+
+/** Approximate the flux across a boundary between quadrants.
+ *
+ * We use a very simple upwind numerical flux.
+ *
+ * This function matches the p4est_iter_face_t prototype used by
+ * p4est_iterate().
+ *
+ * \param [in] info the information about the quadrants on either side of the
+ *                  interface, populated by p4est_iterate()
+ * \param [in] user_data the user_data given to p4est_iterate(): in this case,
+ *                       it points to the ghost_data array, which contains the
+ *                       step3_data_t data for all of the ghost cells, which
+ *                       was populated by p4est_ghost_exchange_data()
+ */
+static void
+step3_upwind_flux (p4est_iter_face_info_t * info, void *user_data)
+{
+  int                 i, j;
+  p4est_t            *p4est = info->p4est;
+  step3_ctx_t        *ctx = (step3_ctx_t *) p4est->user_pointer;
+  step3_data_t       *ghost_data = (step3_data_t *) user_data;
+  step3_data_t       *udata;
+  p4est_quadrant_t   *quad;
+  double              vdotn = 0.;
+  double              uavg;
+  double              q;
+  double              h, facearea;
+  int                 which_face;
+  int                 upwindside;
+  p4est_iter_face_side_t *side[2];
+  sc_array_t         *sides = &(info->sides);
+
+  /* because there are no boundaries, every face has two sides */
+  P4EST_ASSERT (sides->elem_count == 2);
+
+  side[0] = p4est_iter_fside_array_index_int (sides, 0);
+  side[1] = p4est_iter_fside_array_index_int (sides, 1);
+
+  /* which of the quadrant's faces the interface touches */
+  which_face = side[0]->face;
+
+  switch (which_face) {
+  case 0:                      /* -x side */
+    vdotn = -ctx->v[0];
+    break;
+  case 1:                      /* +x side */
+    vdotn = ctx->v[0];
+    break;
+  case 2:                      /* -y side */
+    vdotn = -ctx->v[1];
+    break;
+  case 3:                      /* +y side */
+    vdotn = ctx->v[1];
+    break;
+#ifdef P4_TO_P8
+  case 4:                      /* -z side */
+    vdotn = -ctx->v[2];
+    break;
+  case 5:                      /* +z side */
+    vdotn = ctx->v[2];
+    break;
+#endif
+  }
+  upwindside = vdotn >= 0. ? 0 : 1;
+
+  /* Because we have non-conforming boundaries, one side of an interface can
+   * either have one large ("full") quadrant or 2^(d-1) small ("hanging")
+   * quadrants: we have to compute the average differently in each case.  The
+   * info populated by p4est_iterate() gives us the context we need to
+   * proceed. */
+  uavg = 0;
+  if (side[upwindside]->is_hanging) {
+    /* there are 2^(d-1) (P4EST_HALF) subfaces */
+    for (j = 0; j < P4EST_HALF; j++) {
+      if (side[upwindside]->is.hanging.is_ghost[j]) {
+        /* *INDENT-OFF* */
+        udata =
+          (step3_data_t *) &ghost_data[side[upwindside]->is.hanging.quadid[j]];
+        /* *INDENT-ON* */
+      }
+      else {
+        udata =
+          (step3_data_t *) side[upwindside]->is.hanging.quad[j]->p.user_data;
+      }
+      uavg += udata->u;
+    }
+    uavg /= P4EST_HALF;
+  }
+  else {
+    if (side[upwindside]->is.full.is_ghost) {
+      udata = (step3_data_t *) & ghost_data[side[upwindside]->is.full.quadid];
+    }
+    else {
+      udata = (step3_data_t *) side[upwindside]->is.full.quad->p.user_data;
+    }
+    uavg = udata->u;
+  }
+  /* flux from side 0 to side 1 */
+  q = vdotn * uavg;
+  for (i = 0; i < 2; i++) {
+    if (side[i]->is_hanging) {
+      /* there are 2^(d-1) (P4EST_HALF) subfaces */
+      for (j = 0; j < P4EST_HALF; j++) {
+        quad = side[i]->is.hanging.quad[j];
+        h =
+          (double) P4EST_QUADRANT_LEN (quad->level) / (double) P4EST_ROOT_LEN;
+#ifndef P4_TO_P8
+        facearea = h;
+#else
+        facearea = h * h;
+#endif
+        if (!side[i]->is.hanging.is_ghost[j]) {
+          udata = (step3_data_t *) quad->p.user_data;
+          if (i == upwindside) {
+            udata->dudt += vdotn * udata->u * facearea * (i ? 1. : -1.);
+          }
+          else {
+            udata->dudt += q * facearea * (i ? 1. : -1.);
+          }
+        }
+      }
+    }
+    else {
+      quad = side[i]->is.full.quad;
+      h = (double) P4EST_QUADRANT_LEN (quad->level) / (double) P4EST_ROOT_LEN;
+#ifndef P4_TO_P8
+      facearea = h;
+#else
+      facearea = h * h;
+#endif
+      if (!side[i]->is.full.is_ghost) {
+        udata = (step3_data_t *) quad->p.user_data;
+        udata->dudt += q * facearea * (i ? 1. : -1.);
+      }
+    }
+  }
+}
+
+/** Compute the new value of the state from the computed time derivative.
+ *
+ * We use a simple forward Euler scheme.
+ *
+ * The derivative was computed by a p4est_iterate() loop by the callbacks
+ * step3_quad_divergence() and step3_upwind_flux(). Now we multiply this by
+ * the timestep and add to the current solution.
+ *
+ * This function matches the p4est_iter_volume_t prototype used by
+ * p4est_iterate().
+ *
+ * \param [in] info          the information about this quadrant that has been
+ *                           populated by p4est_iterate()
+ * \param [in] user_data the user_data given to p4est_iterate(): in this case,
+ *                       it points to the timestep.
+ */
+static void
+step3_timestep_update (p4est_iter_volume_info_t * info, void *user_data)
+{
+  p4est_quadrant_t   *q = info->quad;
+  step3_data_t       *data = (step3_data_t *) q->p.user_data;
+  double              dt = *((double *) user_data);
+  double              vol;
+  double              h =
+    (double) P4EST_QUADRANT_LEN (q->level) / (double) P4EST_ROOT_LEN;
+
+#ifdef P4_TO_P8
+  vol = h * h * h;
+#else
+  vol = h * h;
+#endif
+
+  data->u += dt * data->dudt / vol;
+}
+
+/** Reset the approximate derivatives.
+ *
+ * p4est_iterate() has an invariant to the order of callback execution: the
+ * p4est_iter_volume_t callback will be executed on a quadrant before the
+ * p4est_iter_face_t callbacks are executed on its faces.  This function
+ * resets the derivative stored in the quadrant's data before
+ * step3_minmod_estimate() updates the derivative based on the face neighbors.
+ *
+ * This function matches the p4est_iter_volume_t prototype used by
+ * p4est_iterate().
+ *
+ * \param [in] info          the information about this quadrant that has been
+ *                           populated by p4est_iterate()
+ * \param [in] user_data     not used
+ */
+static void
+step3_reset_derivatives (p4est_iter_volume_info_t * info, void *user_data)
+{
+  p4est_quadrant_t   *q = info->quad;
+  step3_data_t       *data = (step3_data_t *) q->p.user_data;
+  int                 j;
+
+  for (j = 0; j < P4EST_DIM; j++) {
+    data->du[j] = (1. / 0.);
+  }
+}
+
+/** For two quadrants on either side of a face, estimate the derivative normal
+ * to the face.
+ *
+ * This function matches the p4est_iter_face_t prototype used by
+ * p4est_iterate().
+ *
+ * \param [in] info          the information about this quadrant that has been
+ *                           populated by p4est_iterate()
+ * \param [in] user_data the user_data given to p4est_iterate(): in this case,
+ *                       it points to the ghost_data array, which contains the
+ *                       step3_data_t data for all of the ghost cells, which
+ *                       was populated by p4est_ghost_exchange_data()
+ */
+static void
+step3_minmod_estimate (p4est_iter_face_info_t * info, void *user_data)
+{
+  int                 i, j;
+  p4est_iter_face_side_t *side[2];
+  sc_array_t         *sides = &(info->sides);
+  step3_data_t       *ghost_data = (step3_data_t *) user_data;
+  step3_data_t       *udata;
+  p4est_quadrant_t   *quad;
+  double              uavg[2];
+  double              h[2];
+  double              du_est, du_old;
+  int                 which_dir;
+
+  /* because there are no boundaries, every face has two sides */
+  P4EST_ASSERT (sides->elem_count == 2);
+
+  side[0] = p4est_iter_fside_array_index_int (sides, 0);
+  side[1] = p4est_iter_fside_array_index_int (sides, 1);
+
+  which_dir = side[0]->face / 2;        /* 0 == x, 1 == y, 2 == z */
+
+  for (i = 0; i < 2; i++) {
+    uavg[i] = 0;
+    if (side[i]->is_hanging) {
+      /* there are 2^(d-1) (P4EST_HALF) subfaces */
+      for (j = 0; j < P4EST_HALF; j++) {
+        quad = side[i]->is.hanging.quad[j];
+        h[i] =
+          (double) P4EST_QUADRANT_LEN (quad->level) / (double) P4EST_ROOT_LEN;
+        if (side[i]->is.hanging.is_ghost[j]) {
+          udata = &ghost_data[side[i]->is.hanging.quadid[j]];
+        }
+        else {
+          udata = (step3_data_t *) side[i]->is.hanging.quad[j]->p.user_data;
+        }
+        uavg[i] += udata->u;
+      }
+      uavg[i] /= P4EST_HALF;
+    }
+    else {
+      quad = side[i]->is.full.quad;
+      h[i] =
+        (double) P4EST_QUADRANT_LEN (quad->level) / (double) P4EST_ROOT_LEN;
+      if (side[i]->is.full.is_ghost) {
+        udata = &ghost_data[side[i]->is.full.quadid];
+      }
+      else {
+        udata = (step3_data_t *) side[i]->is.full.quad->p.user_data;
+      }
+      uavg[i] = udata->u;
+    }
+  }
+  du_est = (uavg[1] - uavg[0]) / ((h[0] + h[1]) / 2.);
+  for (i = 0; i < 2; i++) {
+    if (side[i]->is_hanging) {
+      /* there are 2^(d-1) (P4EST_HALF) subfaces */
+      for (j = 0; j < P4EST_HALF; j++) {
+        quad = side[i]->is.hanging.quad[j];
+        if (!side[i]->is.hanging.is_ghost[j]) {
+          udata = (step3_data_t *) quad->p.user_data;
+          du_old = udata->du[which_dir];
+          if (du_old == du_old) {
+            /* there has already been an update */
+            if (du_est * du_old >= 0.) {
+              if (fabs (du_est) < fabs (du_old)) {
+                udata->du[which_dir] = du_est;
+              }
+            }
+            else {
+              udata->du[which_dir] = 0.;
+            }
+          }
+          else {
+            udata->du[which_dir] = du_est;
+          }
+        }
+      }
+    }
+    else {
+      quad = side[i]->is.full.quad;
+      if (!side[i]->is.full.is_ghost) {
+        udata = (step3_data_t *) quad->p.user_data;
+        du_old = udata->du[which_dir];
+        if (du_old == du_old) {
+          /* there has already been an update */
+          if (du_est * du_old >= 0.) {
+            if (fabs (du_est) < fabs (du_old)) {
+              udata->du[which_dir] = du_est;
+            }
+          }
+          else {
+            udata->du[which_dir] = 0.;
+          }
+        }
+        else {
+          udata->du[which_dir] = du_est;
+        }
+      }
+    }
+  }
+}
+
+/** Compute the maximum state value.
+ *
+ * This function updates the maximum value from the value of a single cell.
+ *
+ * This function matches the p4est_iter_volume_t prototype used by
+ * p4est_iterate().
+ *
+ * \param [in] info              the information about this quadrant that has been
+ *                               populated by p4est_iterate()
+ * \param [in,out] user_data     the user_data given to p4est_iterate(): in this case,
+ *                               it points to the maximum value that will be updated
+ */
+static void
+step3_compute_max (p4est_iter_volume_info_t * info, void *user_data)
+{
+  p4est_quadrant_t   *q = info->quad;
+  step3_data_t       *data = (step3_data_t *) q->p.user_data;
+  double              umax = *((double *) user_data);
+
+  umax = SC_MAX (data->u, umax);
+
+  *((double *) user_data) = umax;
+}
+
+/** Compute the timestep.
+ *
+ * Find the smallest quadrant and scale the timestep based on that length and
+ * the advection velocity.
+ *
+ * \param [in] p4est the forest
+ * \return the timestep.
+ */
+static double
+step3_get_timestep (p4est_t * p4est)
+{
+  step3_ctx_t        *ctx = (step3_ctx_t *) p4est->user_pointer;
+  p4est_topidx_t      t, flt, llt;
+  p4est_tree_t       *tree;
+  int                 max_level, global_max_level;
+  int                 mpiret, i;
+  double              min_h, vnorm;
+  double              dt;
+
+  /* compute the timestep by finding the smallest quadrant */
+  flt = p4est->first_local_tree;
+  llt = p4est->last_local_tree;
+
+  max_level = 0;
+  for (t = flt; t <= llt; t++) {
+    tree = p4est_tree_array_index (p4est->trees, t);
+    max_level = SC_MAX (max_level, tree->maxlevel);
+
+  }
+  mpiret =
+    sc_MPI_Allreduce (&max_level, &global_max_level, 1, sc_MPI_INT,
+                      sc_MPI_MAX, p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  min_h =
+    (double) P4EST_QUADRANT_LEN (global_max_level) / (double) P4EST_ROOT_LEN;
+
+  vnorm = 0;
+  for (i = 0; i < P4EST_DIM; i++) {
+    vnorm += ctx->v[i] * ctx->v[i];
+  }
+  vnorm = sqrt (vnorm);
+
+  dt = min_h / 2. / vnorm;
+
+  return dt;
+}
+
+/** Timestep the advection problem.
+ *
+ * Update the state, refine, repartition, and write the solution to file.
+ *
+ * \param [in,out] p4est the forest, whose state is updated
+ * \param [in] time      the end time
+ */
+static void
+step3_timestep (p4est_t * p4est, double time)
+{
+  double              t = 0.;
+  double              dt = 0.;
+  int                 i;
+  step3_data_t       *ghost_data;
+  step3_ctx_t        *ctx = (step3_ctx_t *) p4est->user_pointer;
+  int                 refine_period = ctx->refine_period;
+  int                 repartition_period = ctx->repartition_period;
+  int                 write_period = ctx->write_period;
+  int                 recursive = 0;
+  int                 allowed_level = P4EST_QMAXLEVEL;
+  int                 allowcoarsening = 1;
+  int                 callbackorphans = 0;
+  int                 mpiret;
+  double              orig_max_err = ctx->max_err;
+  double              umax, global_umax;
+  p4est_ghost_t      *ghost;
+
+  /* create the ghost quadrants */
+  ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+  /* create space for storing the ghost data */
+  ghost_data = P4EST_ALLOC (step3_data_t, ghost->ghosts.elem_count);
+  /* synchronize the ghost data */
+  p4est_ghost_exchange_data (p4est, ghost, ghost_data);
+
+  /* initialize du/dx estimates */
+  p4est_iterate (p4est, ghost, (void *) ghost_data,     /* pass in ghost data that we just exchanged */
+                 step3_reset_derivatives,       /* blank the previously calculated derivatives */
+                 step3_minmod_estimate, /* compute the minmod estimate of each cell's derivative */
+#ifdef P4_TO_P8
+                 NULL,          /* there is no callback for the edges between quadrants */
+#endif
+                 NULL);         /* there is no callback for the corners between quadrants */
+
+  for (t = 0., i = 0; t < time; t += dt, i++) {
+    P4EST_GLOBAL_PRODUCTIONF ("time %f\n", t);
+
+    /* refine */
+    if (!(i % refine_period)) {
+      if (i) {
+        /* compute umax */
+        umax = 0.;
+        /* initialize derivative estimates */
+        p4est_iterate (p4est, NULL, (void *) &umax,     /* pass in ghost data that we just exchanged */
+                       step3_compute_max,       /* blank the previously calculated derivatives */
+                       NULL,    /* there is no callback for the faces between quadrants */
+#ifdef P4_TO_P8
+                       NULL,    /* there is no callback for the edges between quadrants */
+#endif
+                       NULL);   /* there is no callback for the corners between quadrants */
+
+        mpiret =
+          sc_MPI_Allreduce (&umax, &global_umax, 1, sc_MPI_DOUBLE, sc_MPI_MAX,
+                            p4est->mpicomm);
+        SC_CHECK_MPI (mpiret);
+        ctx->max_err = orig_max_err * global_umax;
+        P4EST_GLOBAL_PRODUCTIONF ("u_max %f\n", global_umax);
+
+        /* adapt */
+        p4est_refine_ext (p4est, recursive, allowed_level,
+                          step3_refine_err_estimate, NULL,
+                          step3_replace_quads);
+        p4est_coarsen_ext (p4est, recursive, callbackorphans,
+                           step3_coarsen_err_estimate, NULL,
+                           step3_replace_quads);
+        p4est_balance_ext (p4est, P4EST_CONNECT_FACE, NULL,
+                           step3_replace_quads);
+
+        p4est_ghost_destroy (ghost);
+        P4EST_FREE (ghost_data);
+        ghost = NULL;
+        ghost_data = NULL;
+      }
+      dt = step3_get_timestep (p4est);
+    }
+
+    /* repartition */
+    if (i && !(i % repartition_period)) {
+      p4est_partition (p4est, allowcoarsening, NULL);
+
+      if (ghost) {
+        p4est_ghost_destroy (ghost);
+        P4EST_FREE (ghost_data);
+        ghost = NULL;
+        ghost_data = NULL;
+      }
+    }
+
+    /* write out solution */
+    if (!(i % write_period)) {
+      step3_write_solution (p4est, i);
+    }
+
+    /* synchronize the ghost data */
+    if (!ghost) {
+      ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+      ghost_data = P4EST_ALLOC (step3_data_t, ghost->ghosts.elem_count);
+      p4est_ghost_exchange_data (p4est, ghost, ghost_data);
+    }
+
+    /* compute du/dt */
+    /* *INDENT-OFF* */
+    p4est_iterate (p4est,                 /* the forest */
+                   ghost,                 /* the ghost layer */
+                   (void *) ghost_data,   /* the synchronized ghost data */
+                   step3_quad_divergence, /* callback to compute each quad's
+                                             interior contribution to du/dt */
+                   step3_upwind_flux,     /* callback to compute each quads'
+                                             faces' contributions to du/du */
+#ifdef P4_TO_P8
+                   NULL,                  /* there is no callback for the
+                                             edges between quadrants */
+#endif
+                   NULL);                 /* there is no callback for the
+                                             corners between quadrants */
+    /* *INDENT-ON* */
+
+    /* update u */
+    p4est_iterate (p4est, NULL, /* ghosts are not needed for this loop */
+                   (void *) &dt,        /* pass in dt */
+                   step3_timestep_update,       /* update each cell */
+                   NULL,        /* there is no callback for the faces between quadrants */
+#ifdef P4_TO_P8
+                   NULL,        /* there is no callback for the edges between quadrants */
+#endif
+                   NULL);       /* there is no callback for the corners between quadrants */
+
+    /* synchronize the ghost data */
+    p4est_ghost_exchange_data (p4est, ghost, ghost_data);
+
+    /* update du/dx estimate */
+    p4est_iterate (p4est, ghost, (void *) ghost_data,   /* pass in ghost data that we just exchanged */
+                   step3_reset_derivatives,     /* blank the previously calculated derivatives */
+                   step3_minmod_estimate,       /* compute the minmod estimate of each cell's derivative */
+#ifdef P4_TO_P8
+                   NULL,        /* there is no callback for the edges between quadrants */
+#endif
+                   NULL);       /* there is no callback for the corners between quadrants */
+  }
+
+  P4EST_FREE (ghost_data);
+  p4est_ghost_destroy (ghost);
+}
+
+/** The main step 3 program.
+ *
+ * Setup of the example parameters; create the forest, with the state variable
+ * stored in the quadrant data; refine, balance, and partition the forest;
+ * timestep; clean up, and exit.
+ */
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 recursive, partforcoarsen;
+  sc_MPI_Comm         mpicomm;
+  p4est_t            *p4est;
+  p4est_connectivity_t *conn;
+  step3_ctx_t         ctx;
+
+  /* Initialize MPI; see sc_mpi.h.
+   * If configure --enable-mpi is given these are true MPI calls.
+   * Else these are dummy functions that simulate a single-processor run. */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+
+  /* These functions are optional.  If called they store the MPI rank as a
+   * static variable so subsequent global p4est log messages are only issued
+   * from processor zero.  Here we turn off most of the logging; see sc.h. */
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_ESSENTIAL);
+  p4est_init (NULL, SC_LP_PRODUCTION);
+  P4EST_GLOBAL_PRODUCTIONF
+    ("This is the p4est %dD demo example/steps/%s_step3\n",
+     P4EST_DIM, P4EST_STRING);
+
+  ctx.bump_width = 0.1;
+  ctx.max_err = 2.e-2;
+  ctx.center[0] = 0.5;
+  ctx.center[1] = 0.5;
+#ifdef P4_TO_P8
+  ctx.center[2] = 0.5;
+#endif
+#ifndef P4_TO_P8
+  /* randomly chosen advection direction */
+  ctx.v[0] = -0.445868402501118;
+  ctx.v[1] = -0.895098523991131;
+#else
+  ctx.v[0] = 0.485191768970225;
+  ctx.v[1] = -0.427996381877778;
+  ctx.v[2] = 0.762501176669961;
+#endif
+  ctx.refine_period = 2;
+  ctx.repartition_period = 4;
+  ctx.write_period = 8;
+
+  /* Create a forest that consists of just one periodic quadtree/octree. */
+#ifndef P4_TO_P8
+  conn = p4est_connectivity_new_periodic ();
+#else
+  conn = p8est_connectivity_new_periodic ();
+#endif
+
+  /* *INDENT-OFF* */
+  p4est = p4est_new_ext (mpicomm, /* communicator */
+                         conn,    /* connectivity */
+                         0,       /* minimum quadrants per MPI process */
+                         4,       /* minimum level of refinement */
+                         1,       /* fill uniform */
+                         sizeof (step3_data_t),         /* data size */
+                         step3_init_initial_condition,  /* initializes data */
+                         (void *) (&ctx));              /* context */
+  /* *INDENT-ON* */
+
+  /* refine and coarsen based on an interpolation error estimate */
+  recursive = 1;
+  p4est_refine (p4est, recursive, step3_refine_err_estimate,
+                step3_init_initial_condition);
+  p4est_coarsen (p4est, recursive, step3_coarsen_initial_condition,
+                 step3_init_initial_condition);
+
+  /* Partition: The quadrants are redistributed for equal element count.  The
+   * partition can optionally be modified such that a family of octants, which
+   * are possibly ready for coarsening, are never split between processors. */
+  partforcoarsen = 1;
+
+  /* If we call the 2:1 balance we ensure that neighbors do not differ in size
+   * by more than a factor of 2.  This can optionally include diagonal
+   * neighbors across edges or corners as well; see p4est.h. */
+  p4est_balance (p4est, P4EST_CONNECT_FACE, step3_init_initial_condition);
+  p4est_partition (p4est, partforcoarsen, NULL);
+
+  /* time step */
+  step3_timestep (p4est, 0.1);
+
+  /* Destroy the p4est and the connectivity structure. */
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (conn);
+
+  /* Verify that allocations internal to p4est and sc do not leak memory.
+   * This should be called if sc_init () has been called earlier. */
+  sc_finalize ();
+
+  /* This is standard MPI programs.  Without --enable-mpi, this is a dummy. */
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+  return 0;
+}
diff --git a/example/steps/p4est_step4.c b/example/steps/p4est_step4.c
new file mode 100644
index 0000000..706cd85
--- /dev/null
+++ b/example/steps/p4est_step4.c
@@ -0,0 +1,951 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_step4.c
+ *
+ * This 2D example program solves the Poisson equation using finite elements.
+ * Currently, it works on the unit square.  For more general domains, a
+ * coordinate transformation to physical space would need to be implemented.
+ * This will usually entail using a quadrature instead of exact integration.
+ * The check for boundary nodes would need to be adapted to the new geometry.
+ */
+
+/* p4est has two separate interfaces for 2D and 3D, p4est*.h and p8est*.h.
+ * Most API functions are available for both dimensions.  The header file
+ * p4est_to_p8est.h #define's the 2D names to the 3D names such that most code
+ * only needs to be written once.  In this example, we rely on this. */
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_ghost.h>
+#include <p4est_lnodes.h>
+#include <p4est_vtk.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_ghost.h>
+#include <p8est_lnodes.h>
+#include <p8est_vtk.h>
+#endif
+
+/** List number of possible independent nodes for each hanging node. */
+static const int    corner_num_hanging[P4EST_CHILDREN] =
+#ifndef P4_TO_P8
+{ 1, 2, 2, 1 }
+#else
+{ 1, 2, 2, 4, 2, 4, 4, 1 }
+#endif
+;
+static const int    zero = 0;           /**< Constant zero. */
+static const int    ones = P4EST_CHILDREN - 1;  /**< One bit per dimension. */
+
+/** For each node i of the reference quadrant, corner_num_hanging[i] many. */
+static const int   *corner_to_hanging[P4EST_CHILDREN];
+
+/** Callback function to decide on refinement.
+ *
+ * This function is called for every processor-local quadrant in order; its
+ * return value is understood as a boolean refinement flag.  We refine around a
+ * h = 1/8 block with left front lower corner (5/8, 2/8, 6/8).
+ */
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  /* Compute the integer coordinate extent of a quadrant of length 2^(-3). */
+  const p4est_qcoord_t eighth = P4EST_QUADRANT_LEN (3);
+
+  /* Compute the length of the current quadrant in integer coordinates. */
+  const p4est_qcoord_t length = P4EST_QUADRANT_LEN (quadrant->level);
+
+  /* Refine if the quadrant intersects the block in question. */
+  return ((quadrant->x + length > 5 * eighth && quadrant->x < 6 * eighth) &&
+          (quadrant->y + length > 2 * eighth && quadrant->y < 3 * eighth) &&
+#ifdef P4_TO_P8
+          (quadrant->z + length > 6 * eighth && quadrant->z < 7 * eighth) &&
+#endif
+          1);
+}
+
+/** Right hand side function for the 2D Poisson problem.
+ *
+ * This is the negative Laplace operator acting on the function \a uexact.
+ * \param [in] vxyz    x, y, and z coordinates in physical space.
+ *                     Even for the 2D case z is well defined, but ignored here.
+ * \return             Scalar function value at vxyz.
+ */
+static double
+func_rhs (const double vxyz[3])
+{
+  const double        x = vxyz[0];
+  const double        y = vxyz[1];
+#ifdef P4_TO_P8
+  const double        z = vxyz[2];
+
+  return 128. * (x * (1. - x) * y * (1. - y) +
+                 y * (1. - y) * z * (1. - z) + z * (1. - z) * x * (1. - x));
+#else
+  return 32. * (x * (1. - x) + y * (1. - y));
+#endif
+}
+
+/** Exact solution for the 2D Poisson problem.
+ *
+ * We pick a function with zero Dirichlet boundary conditions on the unit square.
+ * \param [in] vxyz    x, y, and z coordinates in physical space.
+ *                     Even for the 2D case z is well defined, but ignored here.
+ * \return             Scalar function value at vxyz.
+ */
+static double
+func_uexact (const double vxyz[3])
+{
+  const double        x = vxyz[0];
+  const double        y = vxyz[1];
+#ifdef P4_TO_P8
+  const double        z = vxyz[2];
+#endif
+
+  return 4. * 4.
+#ifdef P4_TO_P8
+    * 4 * z * (1. - z)
+#endif
+    * x * (1. - x) * y * (1. - y);
+}
+
+/** Determine the boundary status on the unit square/cube.
+ * \param [in] p4est    Can be used to access the connectivity.
+ * \param [in] tt       The tree number (always zero for the unit square).
+ * \param [in] node     The corner node of an element to be examined.
+ * \return              True for Dirichlet boundary, false otherwise.
+ */
+static int
+is_boundary_unitsquare (p4est_t * p4est, p4est_topidx_t tt,
+                        p4est_quadrant_t * node)
+{
+  /* For this simple connectivity it is sufficient to check x, y (and z). */
+  return (node->x == 0 || node->x == P4EST_ROOT_LEN ||
+          node->y == 0 || node->y == P4EST_ROOT_LEN ||
+#ifdef P4_TO_P8
+          node->z == 0 || node->z == P4EST_ROOT_LEN ||
+#endif
+          0);
+}
+
+/** Decode the information from p{4,8}est_lnodes_t for a given element.
+ *
+ * \see p4est_lnodes.h for an in-depth discussion of the encoding.
+ * \param [in] face_code         Bit code as defined in p{4,8}est_lnodes.h.
+ * \param [out] hanging_corner   Undefined if no node is hanging.
+ *                               If any node is hanging, this contains
+ *                               one integer per corner, which is -1
+ *                               for corners that are not hanging,
+ *                               and the number of the non-hanging
+ *                               corner on the hanging face/edge otherwise.
+ *                               For faces in 3D, it is diagonally opposite.
+ * \return true if any node is hanging, false otherwise.
+ */
+static int
+lnodes_decode2 (p4est_lnodes_code_t face_code,
+                int hanging_corner[P4EST_CHILDREN])
+{
+  if (face_code) {
+    const int           c = (int) (face_code & ones);
+    int                 i, h;
+    int                 work = (int) (face_code >> P4EST_DIM);
+
+    /* These two corners are never hanging by construction. */
+    hanging_corner[c] = hanging_corner[c ^ ones] = -1;
+    for (i = 0; i < P4EST_DIM; ++i) {
+      /* Process face hanging corners. */
+      h = c ^ (1 << i);
+      hanging_corner[h ^ ones] = (work & 1) ? c : -1;
+#ifdef P4_TO_P8
+      /* Process edge hanging corners. */
+      hanging_corner[h] = (work & P4EST_CHILDREN) ? c : -1;
+#endif
+      work >>= 1;
+    }
+    return 1;
+  }
+  return 0;
+}
+
+/** Compute values at hanging nodes by interpolation.
+ * A face hanging node in 3D depends on the four corner nodes of the face,
+ * edge hanging nodes or face hanging nodes in 2D depend on two nodes.
+ * This function works in place, we have to be careful about the ordering.
+ * Face hanging node values are not reused, so they are overwritten first.
+ * \param [in] face_code    This number encodes the child id of the quadrant
+ *                          and the hanging status of faces and edges.
+ * \param [in,out] inplace  On input, the values at the independent nodes.
+ *                          On output, interpolated to hanging node locations.
+ */
+static void
+interpolate_hanging_nodes (p4est_lnodes_code_t face_code,
+                           double inplace[P4EST_CHILDREN])
+{
+  const int           c = (int) (face_code & ones);
+  int                 i, j;
+  int                 ef;
+  int                 work = (int) (face_code >> P4EST_DIM);
+  double              sum;
+  const double        factor = 1. / P4EST_HALF;
+
+  /* Compute face hanging nodes first (this is all there is in 2D). */
+  for (i = 0; i < P4EST_DIM; ++i) {
+    if (work & 1) {
+      ef = p4est_corner_faces[c][i];
+      sum = 0.;
+      for (j = 0; j < P4EST_HALF; ++j) {
+        sum += inplace[p4est_face_corners[ef][j]];
+      }
+      inplace[c ^ ones ^ (1 << i)] = factor * sum;
+    }
+    work >>= 1;
+  }
+
+#ifdef P4_TO_P8
+  /* Compute edge hanging nodes afterwards */
+  for (i = 0; i < P4EST_DIM; ++i) {
+    if (work & 1) {
+      ef = p8est_corner_edges[c][i];
+      inplace[c ^ (1 << i)] = .5 * (inplace[p8est_edge_corners[ef][0]] +
+                                    inplace[p8est_edge_corners[ef][1]]);
+    }
+    work >>= 1;
+  }
+#endif
+}
+
+/** Parallel sum of values in node vector across all sharers.
+ *
+ * This function is necessary in the matrix-vector product since elements
+ * from multiple processors can contribute to any given node value.
+ *
+ * \param [in] p4est      The mesh is not changed.
+ * \param [in] lnodes     The node numbering is not changed.
+ * \param [in,out] v      On input, vector with local contributions.
+ *                        On output, the node-wise sum across all sharers.
+ */
+static void
+share_sum (p4est_t * p4est, p4est_lnodes_t * lnodes, double *v)
+{
+  const int           nloc = lnodes->num_local_nodes;
+  const int           npeers = (int) lnodes->sharers->elem_count;
+  int                 iq, jn;
+  int                 gl;
+  sc_array_t          node_data;
+  p4est_lnodes_rank_t *lrank;
+  p4est_lnodes_buffer_t *buffer;
+
+  sc_array_init_data (&node_data, v, sizeof (double), nloc);
+  buffer = p4est_lnodes_share_all (&node_data, lnodes);
+
+  for (iq = 0; iq < npeers; ++iq) {
+    lrank = (p4est_lnodes_rank_t *) sc_array_index_int (lnodes->sharers, iq);
+    sc_array_t         *recv_data =
+      (sc_array_t *) sc_array_index_int (buffer->recv_buffers, iq);
+    P4EST_ASSERT (recv_data->elem_size == node_data.elem_size);
+
+    if (lrank->rank != p4est->mpirank) {
+      const int           nshared = (int) lrank->shared_nodes.elem_count;
+      const double       *w = (const double *) recv_data->array;
+
+      P4EST_ASSERT ((int) recv_data->elem_count == nshared);
+
+      for (jn = 0; jn < nshared; ++jn) {
+        gl = (int)
+          *(p4est_locidx_t *) sc_array_index_int (&lrank->shared_nodes, jn);
+        P4EST_ASSERT (0 <= gl && gl < nloc);
+        v[gl] += w[jn];
+      }
+    }
+#ifdef P4EST_ENABLE_DEBUG
+    else {
+      P4EST_ASSERT (recv_data->elem_count == 0);
+      P4EST_ASSERT (lrank->owned_offset == 0);
+      P4EST_ASSERT (lrank->owned_count == lnodes->owned_count);
+    }
+#endif
+  }
+
+  p4est_lnodes_buffer_destroy (buffer);
+  sc_array_reset (&node_data);
+}
+
+/** Compute the inner product of two node vectors in parallel.
+ *
+ * \param [in] p4est          The forest is not changed.
+ * \param [in] lnodes         The node numbering is not changed.
+ * \param [in] v1             First node vector.
+ * \param [in] v2             Second node vector.
+ * \return                    Parallel l_2 inner product over the domain.
+ */
+static double
+vector_dot (p4est_t * p4est, p4est_lnodes_t * lnodes,
+            const double *v1, const double *v2)
+{
+  const int           nown = lnodes->owned_count;
+  int                 mpiret;
+  int                 lnid;
+  double              lsum, gsum;
+
+  /* We only sum the locally owned values to avoid double counting. */
+  lsum = 0.;
+  for (lnid = 0; lnid < nown; ++lnid) {
+    lsum += v1[lnid] * v2[lnid];
+  }
+
+  /* The result is made available on all processors. */
+  mpiret = sc_MPI_Allreduce (&lsum, &gsum, 1, sc_MPI_DOUBLE, sc_MPI_SUM,
+                             p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  return gsum;
+}
+
+/** Compute y := y + a * x.
+ *
+ * \param [in] p4est          The forest is not changed.
+ * \param [in] lnodes         The node numbering is not changed.
+ * \param [in] a              The scalar.
+ * \param [in] x              First node vector.
+ * \param [in,out] y          Second node vector.
+ */
+static void
+vector_axpy (p4est_t * p4est, p4est_lnodes_t * lnodes, double a,
+             const double *x, double *y)
+{
+  const int           nloc = lnodes->num_local_nodes;
+  int                 lnid;
+
+  for (lnid = 0; lnid < nloc; ++lnid) {
+    y[lnid] += a * x[lnid];
+  }
+}
+
+/** Compute y := x + b * y.
+ *
+ * \param [in] p4est          The forest is not changed.
+ * \param [in] lnodes         The node numbering is not changed.
+ * \param [in] x              First node vector.
+ * \param [in] b              The scalar.
+ * \param [in,out] y          Second node vector.
+ */
+static void
+vector_xpby (p4est_t * p4est, p4est_lnodes_t * lnodes, const double *x,
+             double b, double *y)
+{
+  const int           nloc = lnodes->num_local_nodes;
+  int                 lnid;
+  double              yy;
+
+  for (lnid = 0; lnid < nloc; ++lnid) {
+    yy = y[lnid];
+    y[lnid] = x[lnid] + b * yy;
+  }
+}
+
+/** Zero all entries of a vector.
+ *
+ * \param [in] p4est          The forest is not changed.
+ * \param [in] lnodes         The node numbering is not changed.
+ * \param [out] x             The vector.
+ */
+static void
+vector_zero (p4est_t * p4est, p4est_lnodes_t * lnodes, double *x)
+{
+  const int           nloc = lnodes->num_local_nodes;
+
+  memset (x, 0., nloc * sizeof (double));
+}
+
+/** copy a vector.
+ *
+ * \param [in] p4est          The forest is not changed.
+ * \param [in] lnodes         The node numbering is not changed.
+ * \param [in] x              Input node vector.
+ * \param [out] y             output node vector.
+ */
+static void
+vector_copy (p4est_t * p4est, p4est_lnodes_t * lnodes, const double *x,
+             double *y)
+{
+  const int           nloc = lnodes->num_local_nodes;
+
+  memcpy (y, x, nloc * sizeof (double));
+}
+
+/** Allocate storage for processor-relevant nodal degrees of freedom.
+ *
+ * \param [in] lnodes   This structure is queried for the node count.
+ * \return              Allocated double array; must be freed with P4EST_FREE.
+ */
+static double      *
+allocate_vector (p4est_lnodes_t * lnodes)
+{
+  return P4EST_ALLOC (double, lnodes->num_local_nodes);
+}
+
+/** Interpolate right hand side and exact solution onto mesh nodes.
+ *
+ * \param [in] p4est          The forest is not changed.
+ * \param [in] lnodes         The node numbering is not changed.
+ * \param [out] rhs_eval      Is allocated and filled with function values.
+ * \param [out] uexact_eval   Is allocated and filled with function values.
+ * \param [out] pbc           Boolean flags for Dirichlet boundary nodes.
+ */
+static void
+interpolate_functions (p4est_t * p4est, p4est_lnodes_t * lnodes,
+                       double **rhs_eval, double **uexact_eval, int8_t ** pbc)
+{
+  const p4est_locidx_t nloc = lnodes->num_local_nodes;
+  int                 anyhang, hanging_corner[P4EST_CHILDREN];
+  int                 i;        /* We use plain int for small loops. */
+  double             *rhs, *uexact;
+  double              vxyz[3];  /* We embed the 2D vertices into 3D space. */
+  int8_t             *bc;
+  p4est_topidx_t      tt;       /* Connectivity variables have this type. */
+  p4est_locidx_t      k, q, Q;  /* Process-local counters have this type. */
+  p4est_locidx_t      lni;      /* Node index relative to this processor. */
+  p4est_tree_t       *tree;     /* Pointer to one octree */
+  p4est_quadrant_t   *quad, *parent, sp, node;
+  sc_array_t         *tquadrants;       /* Quadrant array for one tree */
+
+  rhs = *rhs_eval = allocate_vector (lnodes);
+  uexact = *uexact_eval = allocate_vector (lnodes);
+  bc = *pbc = P4EST_ALLOC (int8_t, nloc);
+  memset (bc, -1, sizeof (int8_t) * nloc);      /* Indicator for visiting. */
+
+  /* We need to compute the xyz locations of non-hanging nodes to evaluate the
+   * given functions.  For hanging nodes, we have to look at the corresponding
+   * independent nodes.  Usually we would cache this information, here we only
+   * need it once and throw it away again.
+   * We also compute boundary status of independent nodes. */
+  for (tt = p4est->first_local_tree, k = 0;
+       tt <= p4est->last_local_tree; ++tt) {
+    tree = p4est_tree_array_index (p4est->trees, tt);   /* Current tree */
+    tquadrants = &tree->quadrants;
+    Q = (p4est_locidx_t) tquadrants->elem_count;
+    for (q = 0; q < Q; ++q, ++k) {
+      /* This is now a loop over all local elements.
+       * Users might aggregate the above code into a more compact iterator. */
+      quad = p4est_quadrant_array_index (tquadrants, q);
+
+      /* We need to determine whether any node on this element is hanging. */
+      anyhang = lnodes_decode2 (lnodes->face_code[q], hanging_corner);
+      if (!anyhang) {
+        parent = NULL;          /* Defensive programming. */
+      }
+      else {
+        /* At least one node is hanging.  We need the parent quadrant to
+         * find the location of the corresponding non-hanging node. */
+        parent = &sp;
+        p4est_quadrant_parent (quad, parent);
+      }
+      for (i = 0; i < P4EST_CHILDREN; ++i) {
+        lni = lnodes->element_nodes[P4EST_CHILDREN * k + i];
+        P4EST_ASSERT (lni >= 0 && lni < nloc);
+        if (bc[lni] < 0) {
+          if (anyhang && hanging_corner[i] >= 0) {
+            /* This node is hanging; access the referenced node instead. */
+            p4est_quadrant_corner_node (parent, i, &node);
+          }
+          else {
+            p4est_quadrant_corner_node (quad, i, &node);
+          }
+
+          /* Determine boundary status of independent node. */
+          bc[lni] = is_boundary_unitsquare (p4est, tt, &node);
+
+          /* Transform per-tree reference coordinates into physical space. */
+          p4est_qcoord_to_vertex (p4est->connectivity, tt, node.x, node.y,
+#ifdef P4_TO_P8
+                                  node.z,
+#endif
+                                  vxyz);
+
+          /* Use physical space coordinates to evaluate functions */
+          rhs[lni] = func_rhs (vxyz);
+          uexact[lni] = func_uexact (vxyz);
+        }
+      }
+    }
+  }
+}
+
+/** Apply a finite element matrix to a node vector, y = Mx.
+ * \param [in] p4est          The forest is not changed.
+ * \param [in] lnodes         The node numbering is not changed.
+ * \param [in] bc             Boolean flags for Dirichlet boundary nodes.
+ *                            If NULL, no special action is taken.
+ * \param [in] stiffness      If false use scaling for the mass matrix,
+ *                            if true use the scaling for stiffness matrix.
+ * \param [in] matrix         A 4x4 matrix computed on the reference element.
+ * \param [in] in             Input vector x.
+ * \param [out] out           Output vector y = Mx.
+ */
+static void
+multiply_matrix (p4est_t * p4est, p4est_lnodes_t * lnodes, const int8_t * bc,
+                 int stiffness,
+                 double (*matrix)[P4EST_CHILDREN][P4EST_CHILDREN],
+                 const double *in, double *out)
+{
+  const int           nloc = lnodes->num_local_nodes;
+  const int           nown = lnodes->owned_count;
+  int                 c, h;
+  int                 i, j, k;
+  int                 q, Q;
+  int                 anyhang, hanging_corner[P4EST_CHILDREN];
+  int                 isboundary[P4EST_CHILDREN];
+  int                 ncontrib;
+  const int          *contrib_corner;
+  double              factor, sum, inloc[P4EST_CHILDREN];
+  sc_array_t         *tquadrants;
+  p4est_topidx_t      tt;
+  p4est_locidx_t      lni, all_lni[P4EST_CHILDREN];
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *quad;
+
+  /* Initialize the output vector. */
+  if (bc == NULL) {
+    /* No boundary values, output vector has all zero values. */
+    for (lni = 0; lni < nloc; ++lni) {
+      out[lni] = 0.;
+    }
+  }
+  else {
+    /* We have boundary conditions, switch on the locally owned nodes. */
+    for (lni = 0; lni < nown; ++lni) {
+      out[lni] = bc[lni] ? in[lni] : 0.;
+    }
+    /* The node values owned by other processors will be added there. */
+    for (lni = nown; lni < nloc; ++lni) {
+      out[lni] = 0.;
+    }
+  }
+
+  /* Loop over local quadrants to apply the element matrices. */
+  for (tt = p4est->first_local_tree, k = 0;
+       tt <= p4est->last_local_tree; ++tt) {
+    tree = p4est_tree_array_index (p4est->trees, tt);
+    tquadrants = &tree->quadrants;
+    Q = (p4est_locidx_t) tquadrants->elem_count;
+    for (q = 0; q < Q; ++q, ++k) {
+      quad = p4est_quadrant_array_index (tquadrants, q);
+
+      /* If we are on the 2D unit square, the Jacobian determinant is h^2;
+       * for the stiffness matrix this cancels with the inner derivatives.
+       * On the 3D unit cube we have an additional power of h. */
+#ifndef P4_TO_P8
+      factor = !stiffness ? pow (.5, 2. * quad->level) : 1.;
+#else
+      factor = pow (.5, (!stiffness ? 3. : 1.) * quad->level);
+#endif
+      for (i = 0; i < P4EST_CHILDREN; ++i) {
+        /* Cache some information on corner nodes. */
+        lni = lnodes->element_nodes[P4EST_CHILDREN * k + i];
+        isboundary[i] = (bc == NULL ? 0 : bc[lni]);
+        inloc[i] = !isboundary[i] ? in[lni] : 0.;
+        all_lni[i] = lni;
+      }
+
+      /* Figure out the hanging corners on this element, if any. */
+      anyhang = lnodes_decode2 (lnodes->face_code[k], hanging_corner);
+
+      if (!anyhang) {
+        /* No hanging nodes on this element; just apply the matrix. */
+        for (i = 0; i < P4EST_CHILDREN; ++i) {
+          if (!isboundary[i]) {
+            sum = 0.;
+            for (j = 0; j < P4EST_CHILDREN; ++j) {
+              sum += (*matrix)[i][j] * inloc[j];
+            }
+            out[all_lni[i]] += factor * sum;
+          }
+        }
+      }
+      else {
+        /* Compute input values at hanging nodes by interpolation. */
+        interpolate_hanging_nodes (lnodes->face_code[k], inloc);
+
+        /* Apply element matrix and then the transpose interpolation. */
+        for (i = 0; i < P4EST_CHILDREN; ++i) {
+          sum = 0.;
+          for (j = 0; j < P4EST_CHILDREN; ++j) {
+            sum += (*matrix)[i][j] * inloc[j];
+          }
+          if (hanging_corner[i] == -1) {
+            /* This node is not hanging, there is no need to transform. */
+            c = 0;
+            ncontrib = 1;
+            contrib_corner = &i;
+            sum *= factor;
+          }
+          else {
+            /* This node is hanging.  Use hanging node relations from the
+             * reference quadrant by transforming corner numbers with ^ c. */
+            c = hanging_corner[i];      /* Child id of quadrant. */
+            ncontrib = corner_num_hanging[i ^ c];
+            contrib_corner = corner_to_hanging[i ^ c];
+            sum *= factor / (double) ncontrib;
+          }
+          /* The result is added into the output vector. */
+          for (j = 0; j < ncontrib; ++j) {
+            h = contrib_corner[j] ^ c;  /* Inverse transform of node number. */
+            if (!isboundary[h]) {
+              out[all_lni[h]] += sum;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /* Parallel sum of result. */
+  share_sum (p4est, lnodes, out);
+}
+
+/** Set Dirichlet boundary values of a node vector to zero.
+ *
+ * \param [in] lnodes         The node numbering is not changed.
+ * \param [in] bc             Boolean flags for Dirichlet boundary nodes.
+ *                            If NULL, this function does nothing.
+ * \param [in,out] v          Dirichlet nodes are overwritten with zero.
+ */
+static void
+set_dirichlet (p4est_lnodes_t * lnodes, const int8_t * bc, double *v)
+{
+  const int           nloc = lnodes->num_local_nodes;
+  p4est_locidx_t      lni;
+
+  if (bc == NULL) {
+    return;
+  }
+  for (lni = 0; lni < nloc; ++lni) {
+    if (bc[lni]) {
+      v[lni] = 0.;
+    }
+  }
+}
+
+/** Multiply the mass matrix with a vector of ones to compute the volume.
+ * \param [in] p4est          The forest is not changed.
+ * \param [in] lnodes         The node numbering is not changed.
+ * \param [in] matrix         The mass matrix should be passed in here.
+ * \param [in] tmp            Must be allocated, entries are undefined.
+ * \param [in,out] lump       Must be allocated, receives matrix * ones.
+ */
+static void
+test_area (p4est_t * p4est, p4est_lnodes_t * lnodes,
+           double (*matrix)[P4EST_CHILDREN][P4EST_CHILDREN],
+           double *tmp, double *lump)
+{
+  const int           nloc = lnodes->num_local_nodes;
+  double              dot;
+  p4est_locidx_t      lni;
+
+  for (lni = 0; lni < nloc; ++lni) {
+    tmp[lni] = 1.;
+  }
+  multiply_matrix (p4est, lnodes, NULL, 0, matrix, tmp, lump);
+  dot = vector_dot (p4est, lnodes, tmp, lump);
+  dot = sqrt (dot);
+
+  P4EST_GLOBAL_PRODUCTIONF ("Area of domain: %g\n", dot);
+}
+
+/** Execute the conjugate gradient method.
+ * \param [in] p4est     The forest is not changed.
+ * \param [in] lnodes    The node numbering is not changed.
+ * \param [in] bc        Boolean flags for Dirichlet boundary nodes.
+ * \param [in] stiffness If false use scaling for the mass matrix,
+ *                       if true use the scaling for stiffness matrix.
+ * \param [in] matrix    The mass matrix should be passed in here.
+ * \param [in] b         The right hand side vector.
+ * \param [out] x        The result; we use an initial value of zero.
+ */
+static void
+solve_by_cg (p4est_t * p4est, p4est_lnodes_t * lnodes, const int8_t * bc,
+             int stiffness,
+             double (*matrix)[P4EST_CHILDREN][P4EST_CHILDREN],
+             const double *b, double *x)
+{
+  const int           imax = 100;
+  const double        tol = 1.e-6;
+
+  int                 i;
+  double              alpha, beta, pAp;
+  double              rr, rrnew, rrorig;
+  double             *aux[4];
+  double             *r, *p, *Ap;
+
+  for (i = 0; i < 3; ++i) {
+    aux[i] = allocate_vector (lnodes);
+  }
+  r = aux[0];
+  p = aux[1];
+  Ap = aux[2];
+
+  /* Initialize the solution vector to zero. */
+  vector_zero (p4est, lnodes, x);
+  vector_copy (p4est, lnodes, b, r);
+  vector_copy (p4est, lnodes, b, p);
+  rr = rrorig = vector_dot (p4est, lnodes, r, r);
+
+  for (i = 0; i < imax && rr > rrorig * tol * tol; i++) {
+    multiply_matrix (p4est, lnodes, bc, stiffness, matrix, p, Ap);
+    pAp = vector_dot (p4est, lnodes, p, Ap);
+    alpha = rr / pAp;
+    vector_axpy (p4est, lnodes, alpha, p, x);
+    vector_axpy (p4est, lnodes, -alpha, Ap, r);
+    rrnew = vector_dot (p4est, lnodes, r, r);
+    beta = rrnew / rr;
+    vector_xpby (p4est, lnodes, r, beta, p);
+    P4EST_GLOBAL_VERBOSEF ("%03d: r'r %g alpha %g beta %g\n",
+                           i, rr, alpha, beta);
+    rr = rrnew;
+  }
+  if (i < imax) {
+    P4EST_GLOBAL_PRODUCTIONF ("cg converged to %g in %d iterations\n",
+                              sqrt (rr), i);
+  }
+  else {
+    P4EST_GLOBAL_PRODUCTIONF ("cg did not converge (%g) in %d iterations\n",
+                              sqrt (rr), imax);
+  }
+
+  /* Free temporary storage. */
+  for (i = 0; i < 3; ++i) {
+    P4EST_FREE (aux[i]);
+  }
+}
+
+/** Execute the numerical part of the example: Solve Poisson's equation.
+ * \param [in] p4est    Solve the PDE with the given mesh refinement.
+ */
+static void
+solve_poisson (p4est_t * p4est)
+{
+  /* 1D mass matrix on the reference element [0, 1]. */
+  static const double m_1d[2][2] = {
+    {1 / 3., 1 / 6.},
+    {1 / 6., 1 / 3.},
+  };
+  /* 1D stiffness matrix on the reference element [0, 1]. */
+  static const double s_1d[2][2] = {
+    {1., -1.},
+    {-1., 1.},
+  };
+  int                 i, j, k, l;
+#ifdef P4_TO_P8
+  int                 m, n;
+#endif
+  double              mass_dd[P4EST_CHILDREN][P4EST_CHILDREN];
+  double              stiffness_dd[P4EST_CHILDREN][P4EST_CHILDREN];
+  double             *rhs_eval, *uexact_eval, *lump;
+  double             *rhs_fe, *u_fe, *u_diff, *diff_mass;
+  double              err2, err;
+  int8_t             *bc;
+  p4est_ghost_t      *ghost;
+  p4est_lnodes_t     *lnodes;
+
+  /* Create the ghost layer to learn about parallel neighbors. */
+  ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+
+  /* Create a node numbering for continuous linear finite elements. */
+  lnodes = p4est_lnodes_new (p4est, ghost, 1);
+
+  /* Destroy the ghost structure -- no longer needed after node creation. */
+  p4est_ghost_destroy (ghost);
+  ghost = NULL;
+
+  /* Compute entries of reference mass and stiffness matrices in 2D.
+   * In this example we can proceed without numerical integration. */
+  for (l = 0; l < 2; ++l) {
+    for (k = 0; k < 2; ++k) {
+      for (j = 0; j < 2; ++j) {
+        for (i = 0; i < 2; ++i) {
+#ifndef P4_TO_P8
+          mass_dd[2 * j + i][2 * l + k] = m_1d[i][k] * m_1d[j][l];
+          stiffness_dd[2 * j + i][2 * l + k] =
+            s_1d[i][k] * m_1d[j][l] + m_1d[i][k] * s_1d[j][l];
+#else
+          for (n = 0; n < 2; ++n) {
+            for (m = 0; m < 2; ++m) {
+              mass_dd[4 * i + 2 * n + m][4 * l + 2 * k + j] =
+                m_1d[m][j] * m_1d[n][k] * m_1d[i][l];
+              stiffness_dd[4 * i + 2 * n + m][4 * l + 2 * k + j] =
+                s_1d[m][j] * m_1d[n][k] * m_1d[i][l] +
+                m_1d[m][j] * s_1d[n][k] * m_1d[i][l] +
+                m_1d[m][j] * m_1d[n][k] * s_1d[i][l];
+            }
+          }
+#endif
+        }
+      }
+    }
+  }
+
+  /* Assign independent nodes for hanging nodes. */
+  corner_to_hanging[0] = &zero;
+#ifdef P4_TO_P8
+  corner_to_hanging[1] = p8est_edge_corners[0];
+  corner_to_hanging[2] = p8est_edge_corners[4];
+  corner_to_hanging[3] = p8est_face_corners[4];
+  corner_to_hanging[4] = p8est_edge_corners[8];
+#endif
+  corner_to_hanging[ones - 2] = p4est_face_corners[2];
+  corner_to_hanging[ones - 1] = p4est_face_corners[0];
+  corner_to_hanging[ones] = &ones;
+
+  /* Test mass matrix multiplication by computing the area of the domain. */
+  rhs_fe = allocate_vector (lnodes);
+  lump = allocate_vector (lnodes);
+  test_area (p4est, lnodes, &mass_dd, rhs_fe, lump);
+
+  /* Interpolate right hand side and exact solution onto mesh nodes. */
+  interpolate_functions (p4est, lnodes, &rhs_eval, &uexact_eval, &bc);
+
+  /* Apply mass matrix to create right hand side FE vector. */
+  multiply_matrix (p4est, lnodes, bc, 0, &mass_dd, rhs_eval, rhs_fe);
+  set_dirichlet (lnodes, bc, rhs_fe);
+
+  /* Run conjugate gradient method with initial value zero. */
+  u_fe = allocate_vector (lnodes);
+  solve_by_cg (p4est, lnodes, bc, 1, &stiffness_dd, rhs_fe, u_fe);
+
+  /* Compute the pointwise difference with the exact vector. */
+  u_diff = allocate_vector (lnodes);
+  vector_copy (p4est, lnodes, u_fe, u_diff);
+  vector_axpy (p4est, lnodes, -1., uexact_eval, u_diff);
+
+  /* Compute the L2 difference with the exact vector.
+   * We know that this is over-optimistic: Quadrature will be sharper.
+   * We could also reuse another vector instead of allocating a new one. */
+  diff_mass = allocate_vector (lnodes);
+  multiply_matrix (p4est, lnodes, bc, 0, &mass_dd, u_diff, diff_mass);
+  err2 = vector_dot (p4est, lnodes, diff_mass, u_diff);
+  err = sqrt (err2);
+  P4EST_GLOBAL_PRODUCTIONF ("||u_fe - u_exact||_L2 = %g\n", err);
+
+  /* Free finite element vectors */
+  P4EST_FREE (diff_mass);
+  P4EST_FREE (u_diff);
+  P4EST_FREE (u_fe);
+  P4EST_FREE (lump);
+  P4EST_FREE (rhs_fe);
+  P4EST_FREE (rhs_eval);
+  P4EST_FREE (uexact_eval);
+  P4EST_FREE (bc);
+
+  /* We are done with the FE node numbering. */
+  p4est_lnodes_destroy (lnodes);
+}
+
+/** The main function of the step4 example program.
+ *
+ * It creates a connectivity and forest, refines it, and solves the Poisson
+ * equation with piecewise linear finite elements.
+ */
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 startlevel, endlevel, level;
+  sc_MPI_Comm         mpicomm;
+  p4est_t            *p4est;
+  p4est_connectivity_t *conn;
+
+  /* Initialize MPI; see sc_mpi.h.
+   * If configure --enable-mpi is given these are true MPI calls.
+   * Else these are dummy functions that simulate a single-processor run. */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+
+  /* These functions are optional.  If called they store the MPI rank as a
+   * static variable so subsequent global p4est log messages are only issued
+   * from processor zero.  Here we turn off most of the logging; see sc.h. */
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_ESSENTIAL);
+  p4est_init (NULL, SC_LP_PRODUCTION);  /* SC_LP_ERROR for silence. */
+  P4EST_GLOBAL_PRODUCTIONF
+    ("This is the p4est %dD demo example/steps/%s_step4\n",
+     P4EST_DIM, P4EST_STRING);
+
+  /* Create a forest that consists of multiple quadtrees/octrees.
+   * This file is compiled for both 2D and 3D: the macro P4_TO_P8 can be
+   * checked to execute dimension-dependent code. */
+#ifndef P4_TO_P8
+  conn = p4est_connectivity_new_unitsquare ();
+  /* More complex domains would require a couple changes. */
+  /* conn = p4est_connectivity_new_moebius (); */
+#else
+  conn = p8est_connectivity_new_unitcube ();
+  /* For 3D computation, the matrix-vector product would need to be extended
+   * by interpolating on both hanging edges and faces. */
+  /* More complex domains would require a couple changes. */
+  /* conn = p8est_connectivity_new_rotcubes (); */
+#endif
+
+  /* Create a forest that is not refined; it consists of the root octant.
+   * The p4est_new_ext function can take a startlevel for a load-balanced
+   * initial uniform refinement.  Here we refine adaptively instead. */
+  startlevel = 0;
+  p4est = p4est_new (mpicomm, conn, 0, NULL, NULL);
+
+  /* Refine the forest iteratively, load balancing at each iteration.
+   * This is important when starting with an unrefined forest */
+  endlevel = 5;
+  for (level = startlevel; level < endlevel; ++level) {
+    p4est_refine (p4est, 0, refine_fn, NULL);
+    /* Refinement has lead to up to 8x more elements; redistribute them. */
+    p4est_partition (p4est, 0, NULL);
+  }
+  if (startlevel < endlevel) {
+    /* For finite elements this corner balance is not strictly required.
+     * We call balance only once since it's more expensive than both
+     * coarsen/refine and partition. */
+    p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+    /* We repartition with coarsening in mind to allow for
+     * partition-independent a-posteriori adaptation (not covered in step4). */
+    p4est_partition (p4est, 1, NULL);
+  }
+
+  /* Write the forest to disk for visualization, one file per processor. */
+  p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_step4");
+
+  /* Execute the numerical mathematics part of the example. */
+  solve_poisson (p4est);
+
+  /* Destroy the p4est and the connectivity structure. */
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (conn);
+
+  /* Verify that allocations internal to p4est and sc do not leak memory.
+   * This should be called if sc_init () has been called earlier. */
+  sc_finalize ();
+
+  /* This is standard MPI programs.  Without --enable-mpi, this is a dummy. */
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+  return 0;
+}
diff --git a/example/steps/p8est_step1.c b/example/steps/p8est_step1.c
new file mode 100644
index 0000000..43ba347
--- /dev/null
+++ b/example/steps/p8est_step1.c
@@ -0,0 +1,37 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_step1.c
+ *
+ * This 3D example program refines a domain based on given image data.
+ *
+ * To avoid redundancy, it shares the source code of the 2D example and changes
+ * it to 3D using the preprocessor defines in p4est_to_p8est.h.
+ *
+ * It is entirely possible to write a 3D-only program without relying on this
+ * mechanism.  In this case use the p8est* header files, functions, and data
+ * structures.
+ */
+
+#include <p4est_to_p8est.h>
+#include "p4est_step1.c"
diff --git a/example/steps/p8est_step2.c b/example/steps/p8est_step2.c
new file mode 100644
index 0000000..20b04f7
--- /dev/null
+++ b/example/steps/p8est_step2.c
@@ -0,0 +1,30 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_step2.c
+ *
+ * This 3D example program refines a domain given by an ABAQUS .inp file.
+ */
+
+#include <p4est_to_p8est.h>
+#include "p4est_step2.c"
diff --git a/example/steps/p8est_step3.c b/example/steps/p8est_step3.c
new file mode 100644
index 0000000..3b39e67
--- /dev/null
+++ b/example/steps/p8est_step3.c
@@ -0,0 +1,44 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_step3.c
+ *
+ * This 3D example program uses p4est to solve a simple advection problem.  It
+ * is numerically very simple, and intended to demonstrate several methods of
+ * interacting with the p4est data after it has been refined and partitioned.
+ * It demonstrates the construction of ghost layers (see p8est_ghost.h) and
+ * communication of ghost-layer data, and it demonstrates iteracting with the
+ * quadrants and quadrant boundaries through the p8est_iterate routine (see
+ * p8est_iterate.h).
+ *
+ * the header file p4est_to_p8est.h defines preprocessor macros that map
+ * 2D p4est routines and objects to their 3D p8est counterparts.  By including
+ * this file and then including the source for the 2D example p4est_step3, we
+ * convert the 2D example to a 3D example.
+ *
+ * It is entirely possible to write a 3D-only program without relying on this
+ * mechanism.  In this case use the p8est* header files, functions, and data
+ * structures.
+ * */
+#include <p4est_to_p8est.h>
+#include "p4est_step3.c"
diff --git a/example/steps/p8est_step4.c b/example/steps/p8est_step4.c
new file mode 100644
index 0000000..872603c
--- /dev/null
+++ b/example/steps/p8est_step4.c
@@ -0,0 +1,37 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_step4.c
+ *
+ * This 3D example program solves the Poisson equation using finite elements.
+ *
+ * To avoid redundancy, it shares the source code of the 2D example and changes
+ * it to 3D using the preprocessor defines in p4est_to_p8est.h.
+ *
+ * It is entirely possible to write a 3D-only program without relying on this
+ * mechanism.  In this case use the p8est* header files, functions, and data
+ * structures.
+ */
+
+#include <p4est_to_p8est.h>
+#include "p4est_step4.c"
diff --git a/example/tetgen/Makefile.am b/example/tetgen/Makefile.am
new file mode 100644
index 0000000..dca21d8
--- /dev/null
+++ b/example/tetgen/Makefile.am
@@ -0,0 +1,31 @@
+
+# This file is part of p4est.
+# Makefile.am in example/tetgen
+# included non-recursively from toplevel directory
+
+dist_p4estdata_DATA += \
+        example/tetgen/p8est_box_tetgen.ele \
+        example/tetgen/p8est_box_tetgen.node
+
+if P4EST_ENABLE_BUILD_2D
+bin_PROGRAMS += \
+        example/tetgen/p4est_write_conn example/tetgen/p4est_read_conn
+example_tetgen_p4est_write_conn_SOURCES = example/tetgen/write_conn2.c
+example_tetgen_p4est_read_conn_SOURCES = example/tetgen/read_conn2.c
+
+LINT_CSOURCES += $(example_tetgen_p4est_write_conn_SOURCES) \
+                 $(example_tetgen_p4est_read_conn_SOURCES)
+endif
+
+if P4EST_ENABLE_BUILD_3D
+bin_PROGRAMS += \
+        example/tetgen/p8est_write_conn example/tetgen/p8est_read_conn \
+        example/tetgen/p8est_read_tetgen
+example_tetgen_p8est_write_conn_SOURCES = example/tetgen/write_conn3.c
+example_tetgen_p8est_read_conn_SOURCES = example/tetgen/read_conn3.c
+example_tetgen_p8est_read_tetgen_SOURCES = example/tetgen/read_tetgen.c
+
+LINT_CSOURCES += $(example_tetgen_p8est_write_conn_SOURCES) \
+                 $(example_tetgen_p8est_read_conn_SOURCES) \
+                 $(example_tetgen_p8est_read_tetgen_SOURCES)
+endif
diff --git a/example/tetgen/p8est_box_tetgen.ele b/example/tetgen/p8est_box_tetgen.ele
new file mode 100644
index 0000000..70f9dc2
--- /dev/null
+++ b/example/tetgen/p8est_box_tetgen.ele
@@ -0,0 +1,1147 @@
+1145  4  1
+    0      27   102    44   139    1
+    1      65    11   148   173    1
+    2     162   114   213   234    1
+    3     147   186   108   203    1
+    4     106    13    49   180    1
+    5      12   134    41   135    1
+    6      39   176   132   190    1
+    7     161   194    56   244    1
+    8      75   228    79   245    1
+    9     189   184   127   227    1
+   10      82    38   118   166    1
+   11     165   236    68     9    1
+   12     104   140   107   141    1
+   13       9   144   112   157    1
+   14     104   141   107   274    1
+   15     113   159   136   196    1
+   16      84   209    76   247    1
+   17      18    10    81    80    1
+   18     208   232   111   245    1
+   19     149   201   109   205    1
+   20     138   187   129   197    1
+   21     144   105   143   202    1
+   22      42    26   135   178    1
+   23     223   230    74   261    0
+   24      45    95    11   128    1
+   25      34    58   170   218    1
+   26      54   110    32   158    1
+   27      17    51     9   157    1
+   28     172   233   212   237    1
+   29     136   159    63   196    1
+   30     131   133    62   101    1
+   31     116   211   110   252    1
+   32     125   231   179   247    1
+   33      83   226   162   233    1
+   34       4   262     5   272    0
+   35     121   237   223   238    1
+   36     146   198   105   202    1
+   37      11    95    96   128    1
+   38     113   136   194   228    1
+   39     170   214   163   219    1
+   40       0     2     3   269    0
+   41     148   185    65   222    1
+   42      32   116    54   164    1
+   43      14   131    39   132    1
+   44       8   116    53   119    1
+   45      18    81    10   115    1
+   46     153   206   139   240    1
+   47      86     9    85    17    1
+   48      56   194    33   124    1
+   49     111   161    56   244    1
+   50     208   244    75   245    1
+   51     152   150   151    15    1
+   52     135   124   194   229    1
+   53      52   140   104   141    1
+   54       5   264   260   270    0
+   55      93    43     8   137    1
+   56      78   107   106   140    1
+   57     134   136    63   102    1
+   58      42   227   133   251    1
+   59     189   183   127   184    1
+   60      41    26   135   193    1
+   61     118   103   153   206    1
+   62      38   249   206   263    0
+   63       8    94    93   137    1
+   64      60   177   122   230    1
+   65     136    12    63   159    1
+   66     216   235   147   277    0
+   67      74   230   226   257    0
+   68       4   267     6   271    0
+   69     204   152    15   225    1
+   70      97   219   214   255    1
+   71       0     1     5   273    0
+   72      50   246   209   247    0
+   73     104   140    52   208    0
+   74     162    40   213   233    1
+   75      15    88    66   150    1
+   76     127   183   133   192    1
+   77       0     4     6   271    0
+   78     125   179   152   225    1
+   79      44   160   153   211    1
+   80     133   227   127   251    1
+   81     133   183    62   192    1
+   82      55    33   156   111    1
+   83      12    20    90    89    1
+   84      24   163    38   168    1
+   85     228   262   259   278    0
+   86      27    44   136   196    1
+   87      93     8    43   154    1
+   88      23    88    87    15    1
+   89     204    60    35   122    1
+   90     142    64   143   198    1
+   91     130   213    61   100    1
+   92     153   211   206   240    0
+   93      76   257   231   272    0
+   94     223   261    74   266    0
+   95      69   124   194   224    1
+   96      55   156    13   207    1
+   97      46   198   186   202    1
+   98      14    91    39   131    1
+   99     121   223   174   238    1
+  100     206   240   211   276    0
+  101     150   151    47   149    1
+  102      31    52   144   200    1
+  103     122   177    83   230    1
+  104     186   198   146   202    1
+  105      38    24   129   163    1
+  106     101   191   133   256    1
+  107     109   145    50   246    1
+  108      29   149    47   221    1
+  109      73   246   205   270    0
+  110      10    37    18   115    1
+  111      84   227   184   231    1
+  112     189   133   132   227    0
+  113     100    40    25   188    1
+  114      27   160    44   196    1
+  115     236   157   112   158    1
+  116      20    41    12   134    1
+  117      74   233    83   238    1
+  118     188   131   191   132    1
+  119      47   151    15   175    1
+  120     204    35   125    71    1
+  121      49   145   106   181    1
+  122      84   209   182   229    1
+  123      71   125   204   225    1
+  124      78   140   106   246    1
+  125      35   122    60   177    1
+  126     137   153   139    43    1
+  127     118   119    36   168    1
+  128      73   146   108   201    1
+  129     211   252    79   253    1
+  130      48   185   149   243    1
+  131     214   255   212   266    0
+  132     104   155    67   207    1
+  133      10    70    81   114    1
+  134     140   146   105   202    1
+  135      20    12    90   134    1
+  136     143   165   117   215    1
+  137      74   226    83   233    1
+  138      72   240   195   241    1
+  139      19    11    57    95    1
+  140     106   180    49   181    1
+  141      10    81    80   114    1
+  142      26   178    42   192    1
+  143     218   235    58   255    1
+  144     152   179    66   225    1
+  145      96    65    11   128    1
+  146      99    98    13   180    1
+  147     133   192    42   251    1
+  148     212   213    10   129    0
+  149      60   184   189   227    1
+  150     132   188    39   190    1
+  151     126   156    13   180    1
+  152     139   240   206   241    1
+  153       1   260     5   273    0
+  154      31   105    52   141    1
+  155      48   174   151   221    1
+  156     133   101    26   192    1
+  157      35   177    60   184    1
+  158     152   205    50   247    0
+  159      46   198   143   215    1
+  160     195   259   240   271    0
+  161      79   252    75   253    1
+  162     142   144   143     9    1
+  163     175   204    15    71    1
+  164      79   160   113   228    1
+  165      57    34   216   121    1
+  166     106   107   104   140    1
+  167      75   259   228   273    0
+  168      33    55   156   182    1
+  169      47   149   151   221    1
+  170     108   185   148   203    1
+  171     128    65   148   185    1
+  172      20    89    12   224    1
+  173      27   139    44   210    1
+  174       4   259     0   273    0
+  175      19    96    11    95    1
+  176      37   163   129   197    1
+  177     127   227    84   251    1
+  178     148    11    65   128    1
+  179      26    42   135   193    1
+  180     262   264   209   275    0
+  181      17     9    85   157    1
+  182      16    94     8    53    1
+  183       9    85    68   165    1
+  184     152    15   151   204    0
+  185      40   226   191   254    0
+  186      10    81    70   115    1
+  187     153   160    44   210    1
+  188     259   265     0   273    0
+  189     126   180   106   181    1
+  190      79   211   160   228    1
+  191     149   205   151   243    1
+  192      73   260   140   270    0
+  193     133    26    42   192    1
+  194      46   202   186   203    1
+  195     133    42    26   101    1
+  196     262   228   259   273    0
+  197     114   212    10    70    1
+  198      75   242    79   253    1
+  199     195   267   256   278    0
+  200     104   107    67   274    1
+  201      31   155    52   200    1
+  202      30   179    50   181    1
+  203      66    88    15   225    1
+  204      92    91    22   176    1
+  205      48   230   223   261    0
+  206      92    91    14   131    1
+  207      35   189   184    59    1
+  208     189   127   133   227    1
+  209     204    60   122   230    1
+  210     142   144     9    51    1
+  211      78   140   105   141    1
+  212       8    94    36   119    1
+  213     133   132   131    14    1
+  214      15    87    71   225    1
+  215     148   147   128   203    1
+  216     136    63   102   196    1
+  217      24   138    38   197    1
+  218     148   147   203   277    0
+  219     163   214    82   219    1
+  220     211   252   116   276    0
+  221      78   146    73   201    1
+  222     133   191   132   227    0
+  223     216   169   120   218    1
+  224      60   125    35   184    1
+  225     179   247    50   248    1
+  226      75   244   208   273    0
+  227      53   116    32   164    1
+  228       0     3     1   269    0
+  229     148   185    48   243    1
+  230     206   259   240   276    0
+  231     136   113    12   159    1
+  232     213    25    40   234    1
+  233      43   154   153   210    1
+  234      21    98    99   180    1
+  235     204    15    71   225    1
+  236      16     8    94    93    1
+  237     136    44    27   102    1
+  238     227   250    42   256    0
+  239     175   204    71   122    1
+  240     249   254    72   271    0
+  241     147   108   128   203    1
+  242      38    82   118   168    1
+  243     156   208   111   244    1
+  244      38   167    24   168    1
+  245      53    94     8   119    1
+  246     151   174    48   230    1
+  247      39   188    25   190    1
+  248     132   226   189   227    0
+  249     104   111   156   208    1
+  250     147   169    45   217    1
+  251      52   155   104   208    1
+  252     249   263   254   271    0
+  253     102    63    27   196    1
+  254     240   259   211   276    0
+  255     183   189    59   184    1
+  256       5   260     1   270    0
+  257     156   126   106   209    1
+  258     211   259   252   276    0
+  259      45   186   147   217    1
+  260      49    98    13   107    1
+  261      58   121    34   172    1
+  262     274    67   104   155    1
+  263     259   262     4   278    0
+  264      54   166   116   276    0
+  265      52   105   144   202    1
+  266     152    50    30   179    1
+  267      54   117    32   164    1
+  268       4   262   259   273    0
+  269      23    88    15   150    1
+  270     100   187    40   188    1
+  271      15    47    23   150    1
+  272     151   205   204   230    0
+  273     246   247   205   264    0
+  274     204   151   175    15    1
+  275      50   109    30   145    1
+  276      45   147    11   169    1
+  277     152    30    50   109    1
+  278      32    54   116   110    1
+  279      44   228   211   259    0
+  280      40   187   100   213    1
+  281     129   138    38   249    1
+  282     112   157   144   200    1
+  283      34    57   216   218    1
+  284     136   240   195   278    0
+  285     186   202   108   203    1
+  286     115   212    70    10    1
+  287     105   140    52   141    1
+  288     146   202    73   203    1
+  289       3   266   261   277    0
+  290      28   215    46   217    1
+  291     195   240    72   271    0
+  292     143   198    46   202    1
+  293      28   186    45   217    1
+  294      35    60   204   125    1
+  295     106   209   126   248    1
+  296      14   176    92   183    1
+  297      30   145    49   181    1
+  298     110   158    54   253    1
+  299     204   125    60   231    1
+  300     120   219    97   255    1
+  301     108   202   146   203    1
+  302      32    54   236   117    1
+  303     106   209    50   246    0
+  304      14    62    92   131    1
+  305      56   111    33   161    1
+  306      99    67    13   107    1
+  307     199   242   144   260    0
+  308     153   154   110   210    1
+  309     258   261     3   270    0
+  310     187   249   214   263    0
+  311      44   139   153   210    1
+  312     115   212   170    70    1
+  313      36   167   118   168    1
+  314      38   138    24   167    1
+  315      38   163    82   168    1
+  316      34   212   172    70    1
+  317      42   250   135   256    0
+  318      58   216    34   121    1
+  319     129   187   138   249    1
+  320      24   167    36   168    1
+  321      19    11    96   173    1
+  322     230   257    74   261    0
+  323      65   185    29   222    1
+  324      14   132    39   176    1
+  325     135   178    41   224    1
+  326     189   176   123    14    1
+  327      11    45   128   147    1
+  328     194    33   124    69    1
+  329     194   161    33    69    1
+  330      28   186    46   198    1
+  331     148   223   203   243    0
+  332      83   174   122   230    1
+  333      44   160    27   210    1
+  334     103   139   153   206    1
+  335      52    31   144   105    1
+  336     108   146    73   203    1
+  337      84   227    76   250    1
+  338     103   138    77   206    1
+  339     148   203   185   243    1
+  340      25   100   213    40    1
+  341       7   264   257   272    0
+  342      13    98    99   107    1
+  343     104    13    67   107    1
+  344      13   106    49   107    1
+  345      17    86     9   142    1
+  346      64   143     9   142    1
+  347       9    51    17   142    1
+  348      82   166    97   219    1
+  349     105   140    78   146    1
+  350     150   152   109    66    1
+  351     107   140    78   141    1
+  352     104   106    13   107    1
+  353      10    37   115   129    1
+  354     147   148   128    11    1
+  355      64   165   143   215    1
+  356      83   223    74   238    1
+  357     116    53    32   110    1
+  358     129   130    37   197    1
+  359     194   113   159    12    1
+  360      80    61    10   114    1
+  361      33    56   156   111    1
+  362      72   256   195   271    0
+  363     236    32   117    68    1
+  364     257   264   231   272    0
+  365      13   126   106   156    1
+  366      31   105    51   200    1
+  367     144   105    31   200    1
+  368      12    90    63   159    1
+  369     153   139    44   240    1
+  370      12   136   194   113    1
+  371      33   194    56   161    1
+  372      71    87    15   175    1
+  373      83   233   162   238    1
+  374      37   115   129   163    1
+  375      12    89    69   224    1
+  376     120   171    97   219    1
+  377      64     9   143   165    1
+  378      53   119   116   164    1
+  379     104   155   111   208    1
+  380      74   237   223   266    0
+  381      73   202   140   260    0
+  382     236   143   165   117    1
+  383     116   252    54   276    0
+  384     135    26    41   178    1
+  385      14    91    92   176    1
+  386      36   137   118   167    1
+  387      48   149    29   221    1
+  388      29   185    48   222    1
+  389       8    36   118   119    1
+  390       9    86    85   165    1
+  391      64    86     9   165    1
+  392     165   236   117    68    1
+  393      11    57    95   169    1
+  394      34   212    58   172    1
+  395      13   180    55   207    1
+  396      21   180    99   207    1
+  397     116     8   118   119    1
+  398     173   216   121    57    1
+  399     235   255    97   269    0
+  400      57   169   216   218    1
+  401      48   174    29   222    1
+  402      84   184   125   231    1
+  403      75   244   228   245    1
+  404      23    15    87   175    1
+  405     148   174    48   222    1
+  406     127   184    84   227    1
+  407     144   202   199   260    0
+  408     116   110    54   252    1
+  409      15   152    66   225    1
+  410      60   226   177   230    1
+  411      92   176    22   183    1
+  412      30    66   152   179    1
+  413     156    56    33   182    1
+  414      59   176    14   183    1
+  415     213   212   114   233    1
+  416      29   149    48   185    1
+  417     132   191    40   226    0
+  418      13   156    55   180    1
+  419      14    92    62   183    1
+  420      54   252   110   253    1
+  421     182   209    56   229    1
+  422      22   176    59   183    1
+  423      84   182   124   229    1
+  424     161   244   111   245    1
+  425     263   271   206   276    0
+  426       7   257     6   272    0
+  427      18    10    80   130    1
+  428      18    37    10   130    1
+  429      61    80    10   130    1
+  430     187   129   130   213    1
+  431     111   156    55   207    1
+  432     212   214   170   255    1
+  433      37   129    10   130    1
+  434     136   102    27   196    1
+  435     144   199   112   242    1
+  436      55   180    21   207    1
+  437      35   189    59   177    1
+  438      14   132   123   189    1
+  439     176   189    59    14    1
+  440     101    62    26   192    1
+  441      14   133   132   189    0
+  442      75   211    79   228    1
+  443     101    42    26   193    1
+  444     155   200   242    52    1
+  445     133    62   101   192    1
+  446     120   235   218   255    1
+  447     131   133    14    62    1
+  448      63    90    12   134    1
+  449     136   135   134    12    1
+  450     159   194    12    69    1
+  451      38   138   129   197    1
+  452     144   242    52   260    0
+  453     127   192   133   251    1
+  454     155   208    52   242    1
+  455     177   226    83   230    1
+  456      24   163    37   197    1
+  457     212   115   129    10    1
+  458     129   187   100   197    1
+  459     100   130   129   197    1
+  460     129   163    24   197    1
+  461      52   202   144   260    0
+  462     134   136    12    63    1
+  463      36    94     8   137    1
+  464       9    86    64   142    1
+  465     152   231   205   247    0
+  466     106   145    78   246    1
+  467      36     8   118   137    1
+  468       8   103   118   137    1
+  469      82   119   118   168    1
+  470     244   262   228   278    0
+  471      11   169   147   216    1
+  472     134   193   195   102    1
+  473      99   180    13   207    1
+  474     111   155   104   207    1
+  475      77   187   100   188    1
+  476     132   131    39   188    1
+  477     142   144    51   105    1
+  478     211   228    75   259    0
+  479     148   223   216   277    0
+  480     178   229   135   250    1
+  481      84   247   125   248    1
+  482       1   265     0   269    0
+  483     153   211   116   276    0
+  484     126   182    84   209    1
+  485      73   146   140   202    1
+  486      68    85     9   157    1
+  487      58   237   212   255    0
+  488     236    54    32   158    1
+  489     151    48   149   243    1
+  490     108   186   146   202    1
+  491     144   142   143   105    1
+  492     105   198   143   202    1
+  493      55   180   156   182    1
+  494      49   107   106   145    1
+  495      84   231   125   247    1
+  496     106   107    78   145    1
+  497      56   124    33   182    1
+  498      78   140    73   146    1
+  499     216   121    58   237    1
+  500     120   218   170   255    1
+  501      79   228   113   245    1
+  502     230   231    60   257    0
+  503     156   111    56   244    1
+  504     151   175    47   221    1
+  505      72   191    77   256    1
+  506      23    47    15   175    1
+  507     202   220   199   258    0
+  508     140   105    52   202    1
+  509      45   128   147   186    1
+  510      46   186   147   203    1
+  511     147   128   108   186    1
+  512     161   228   194   244    1
+  513     131   188   191   101    1
+  514      65   173   148   222    1
+  515     237   255    58   266    0
+  516     261   266   223   277    0
+  517     150   151    15    47    1
+  518      91    14    39   176    1
+  519     151   204   175   122    1
+  520     152    30   109    66    1
+  521     106   126    13   180    1
+  522      98    49    13   180    1
+  523      54   199   253   158    1
+  524     150   152    66    15    1
+  525      74   237   233   238    1
+  526      79   242   112   253    1
+  527     153   103   137   139    1
+  528     103   153   137     8    1
+  529       8   118   103   153    1
+  530     118   167    38   168    1
+  531     137   153    43     8    1
+  532     113   194   159   161    1
+  533      16    53     8   154    1
+  534       8    93    16   154    1
+  535       8   153    43   154    1
+  536     110   116    53   154    1
+  537      53   116     8   154    1
+  538      38   214   129   249    0
+  539     150   149   205   151    1
+  540     159   194    69   161    1
+  541     110   153   116   154    1
+  542     116   153     8   154    1
+  543     129   214   187   249    0
+  544      50   209   106   248    1
+  545      52   274    31   141    1
+  546     140   260   208   275    0
+  547     274   104    52   155    1
+  548     121   172    58   238    1
+  549     156   182   126   209    1
+  550     150   152   151   205    1
+  551      51   144     9   157    1
+  552      89    90    12   159    1
+  553      69    89    12   159    1
+  554     130   213    10    61    1
+  555      51   105   144   200    1
+  556     144   157    51   200    1
+  557       1   265   260   273    0
+  558     157   236    68   158    1
+  559     157   236   112     9    1
+  560     100   187   138   197    1
+  561     199   242    75   253    1
+  562       3   261   258   277    0
+  563      64   198    28   215    1
+  564      76   209    84   229    1
+  565     213   100    25   234    1
+  566      25   100    61   234    1
+  567     114   212    70   172    1
+  568      24    38   129   197    1
+  569      65    96    11   173    1
+  570      40    25   162   234    1
+  571      61   100   213   234    1
+  572     212   213   114    10    1
+  573      19    57    11   173    1
+  574     173   216    57    11    1
+  575     118   137   103   167    1
+  576     148   216   173    11    1
+  577     216    58    34   218    1
+  578     223   237    74   238    1
+  579     213   187   212   233    0
+  580     201   205   149   243    1
+  581      46   186    28   217    1
+  582      54   199   158   236    1
+  583     213    40   187   233    0
+  584     124    56   194   229    1
+  585     206   240    72   241    1
+  586     149   185   108   243    1
+  587      95    45    11   169    1
+  588     118   119    82   166    1
+  589     116   119   118   166    1
+  590      54   164   116   166    1
+  591     124   229   178   250    1
+  592     151   149    48   221    1
+  593      28   198    46   215    1
+  594     122   175   151   221    1
+  595      82   163    38   166    1
+  596     116   164   119   166    1
+  597     119   164    82   166    1
+  598     136   228    44   240    0
+  599     220   239   199   265    0
+  600     143   198    64   215    1
+  601     216   148   173   121    1
+  602      11    57   169   216    1
+  603      48   185   148   222    1
+  604      29   174    48   221    1
+  605     212    34    58   170    1
+  606     213    10    61   234    1
+  607     148   173   121   222    1
+  608      56   244   229   262    0
+  609     239   265   220   269    0
+  610     124   178   135   224    1
+  611      12   135    41   224    1
+  612     194    12    69   224    1
+  613      12    41    20   224    1
+  614      42   192   178   251    1
+  615     123   132    14   176    1
+  616     176   189   123   177    1
+  617     108   128   148   185    1
+  618     176   189   177    59    1
+  619      15    88    87   225    1
+  620     113   194   161   228    1
+  621     187   214   212   233    0
+  622     174   223    48   230    1
+  623     204   122   151   230    1
+  624     111   208   155   232    1
+  625     153   110   116   211    1
+  626     170   218    58   255    1
+  627      58   237   121   238    1
+  628     162   226    40   233    1
+  629     156   180   126   182    1
+  630      50   145    30   181    1
+  631     106   145    50   181    1
+  632     244   262   209   275    0
+  633     155   232   208   242    1
+  634     178   250    42   251    1
+  635     212   255   237   266    0
+  636     133    14    62   183    1
+  637     133   127    14   183    1
+  638     135   134    41   193    1
+  639      75   232   208   245    1
+  640     160   211    44   228    1
+  641     183   189   127    14    1
+  642     183   189    14    59    1
+  643      60   177   189   184    1
+  644      35   189   177   184    1
+  645      56   209   156   244    0
+  646     143   236     9   144    0
+  647     213   212   187   129    0
+  648     170   219   120   255    1
+  649     170    58   212   255    1
+  650     256   267   250   278    0
+  651     147   186    46   217    1
+  652      97   171    82   219    1
+  653     104   156   111   207    1
+  654      42   178   135   250    1
+  655     187   213   130   100    1
+  656      14   127   133   189    1
+  657     132   176   123   190    1
+  658     142   143   105   198    1
+  659     213   130    10   129    1
+  660     129   130   100   187    1
+  661     213   114    10   234    1
+  662      77   138   100   187    1
+  663      76   229    84   250    1
+  664      40   162    25   190    1
+  665      25   188    40   190    1
+  666      40   188   132   190    1
+  667     138   206    38   249    1
+  668      73   205   201   243    1
+  669       1   260   258   270    0
+  670      84   250   124   251    1
+  671     191   226   132   227    0
+  672       6   267   254   271    0
+  673       1     3     7   270    0
+  674     132   188    40   191    1
+  675       0   263     2   269    0
+  676      58   266   235   277    0
+  677      40   188   187   191    1
+  678     193   134   195   135    1
+  679     132   133   131   191    1
+  680     133   101   131   191    1
+  681      72   187    77   191    1
+  682     187   188    77   191    1
+  683      83   226    74   230    1
+  684      77   188   101   191    1
+  685       3   261     7   270    0
+  686     233   237    74   268    0
+  687     254   257   191   267    0
+  688     214   219   170   255    1
+  689     205   230   151   261    0
+  690      77   206    72   241    1
+  691      52   274   155    31    1
+  692     227   250    84   251    1
+  693      83   223   174   230    1
+  694     109   201   145   246    1
+  695      97   263   166   269    0
+  696      72   195    77   241    1
+  697     205   231   230   257    0
+  698     200   155   242   232    1
+  699     135   136   134   195    1
+  700     136   102   134   195    1
+  701     139   206   103   241    1
+  702      13   156   104   207    1
+  703       2   266     3   269    0
+  704     226   230    60   257    0
+  705     205   257   230   261    0
+  706      67    99    13   207    1
+  707     104    67    13   207    1
+  708     231   257   205   264    0
+  709     212   187   129   214    0
+  710     103   206    77   241    1
+  711     128   108   148   203    1
+  712     102   195   136   240    1
+  713      48   174   148   223    1
+  714      73   201   108   243    1
+  715     236   112     9   144    1
+  716      56   182   156   209    1
+  717      75   252   199   253    1
+  718     112   199   144   236    1
+  719     112   236   158   199    1
+  720     257   267     6   272    0
+  721      34   212    70   170    1
+  722     149   150   205   109    1
+  723     112   199   158   253    1
+  724     110   211   160   253    1
+  725     150   109   152   205    1
+  726     143   199   144   202    0
+  727     109    50   152   205    1
+  728     205   246    50   247    0
+  729     261   264     7   270    0
+  730      48   223   148   243    0
+  731     140   208   104   275    0
+  732     233   237   172   238    1
+  733       7   264     5   270    0
+  734     108   201   149   243    1
+  735     209   244    56   262    0
+  736     204   122    35    71    1
+  737     202   258   199   260    0
+  738     199   239    54   265    0
+  739      58   235   216   277    0
+  740     208   232    75   242    1
+  741     148   121   216   223    1
+  742     204   230   205   231    0
+  743     194   229    56   244    0
+  744      73   258   202   270    0
+  745     223   243    48   261    0
+  746     212    58   172   237    1
+  747     199   236    54   239    0
+  748      73   140    78   246    1
+  749      43   139    27   210    1
+  750     204   151   152   205    0
+  751     214   249    38   263    0
+  752     204   125   152   225    1
+  753     113   228   161   245    1
+  754      44   240   228   259    0
+  755     153   139    43   210    1
+  756     179   231   152   247    1
+  757      54   239   166   265    0
+  758       2   263   254   266    0
+  759     194   124   135   224    1
+  760     135    12   194   224    1
+  761      60   230   204   231    0
+  762     204   205   152   231    0
+  763       1   258     3   270    0
+  764      50   179   152   247    1
+  765     103   167   138   206    1
+  766     118   167   103   206    1
+  767     138   167    38   206    1
+  768      38   167   118   206    1
+  769     102   240   139   241    1
+  770      77   195   193   241    1
+  771     195   240   102   241    1
+  772     193   195   102   241    1
+  773     124   182    56   229    1
+  774     118   206   153   276    0
+  775      54   265   166   276    0
+  776       7   261   257   264    0
+  777     209   247   246   264    0
+  778      76   231    84   247    1
+  779     108   203    73   243    1
+  780     185   203   108   243    1
+  781     228   229   194   244    0
+  782     206   211   153   276    0
+  783      42   250   227   251    1
+  784     110   210   160   211    1
+  785     160   210   153   211    1
+  786     153   210   110   211    1
+  787     160   211    79   253    1
+  788     220   235    97   258    0
+  789     135   178   124   229    1
+  790     229   267   262   278    0
+  791     266   235   258   269    0
+  792      84   229   124   250    1
+  793       1     7     5   270    0
+  794      38   163   129   214    1
+  795      38   166   163   214    1
+  796     135   195   193   256    1
+  797     261   266     3   268    0
+  798     163   166    82   214    1
+  799     143   202    46   220    0
+  800     115   212   129   214    1
+  801     115   170   212   214    1
+  802     129   163   115   214    1
+  803     163   170   115   214    1
+  804     220   199   236   143    0
+  805     216   223   121   237    1
+  806      52   208   140   242    0
+  807      82   214   166   219    1
+  808     166   214    97   219    1
+  809     199   144   236   143    0
+  810     199   202   143   220    0
+  811      77   195    72   256    1
+  812     136    44   102   240    1
+  813     260   264     5   275    0
+  814     250   262   229   267    0
+  815     135   193    42   256    1
+  816     156   209   106   275    0
+  817     166   214    38   263    0
+  818     254   271   256    72    0
+  819     133    42   101   256    1
+  820     140   242   208   260    0
+  821     236   117   220   143    1
+  822     143   236   165     9    1
+  823      46   215   143   220    1
+  824     157   236     9    68    1
+  825      44   211   153   240    0
+  826       3   266   258   269    0
+  827     117   215   171   220    1
+  828     143   215   117   220    1
+  829      46   217   215   220    1
+  830     215   217   171   220    1
+  831     171   217   120   220    1
+  832     236   158    32    68    1
+  833     172   237    58   238    1
+  834     138   187    77   249    1
+  835      76   227    84   231    1
+  836     152   179   125   231    1
+  837     121   222   174   223    1
+  838     174   222   148   223    1
+  839     148   222   121   223    1
+  840     174   223    83   238    1
+  841     112   242   199   253    1
+  842     204   152   125   231    1
+  843     203   243   223   277    0
+  844      76   264   209   272    0
+  845     110   252   211   253    1
+  846     189   132   123   226    1
+  847      40   162   213   234    1
+  848     132   190   123   226    1
+  849      40   190   132   226    1
+  850     123   177   189   226    1
+  851     189   177    60   226    1
+  852     123   190   162   226    1
+  853     162   190    40   226    1
+  854     123   162    83   226    1
+  855      10   114    61   234    1
+  856      83   177   123   226    1
+  857     106   181    50   248    1
+  858     184   227    60   231    1
+  859     125   184    60   231    1
+  860      77   187    72   249    1
+  861     258   203   261   270    0
+  862     252   265    54   276    0
+  863      50   247   209   248    1
+  864      73   203   202   258    0
+  865     124   250   178   251    1
+  866      56   229   209   262    0
+  867       4   267   262   272    0
+  868     160   196   113   228    1
+  869     113   196   136   228    1
+  870      44   196   160   228    1
+  871     136   196    44   228    1
+  872     111   244   208   245    1
+  873     228   244   161   245    1
+  874      79   232    75   245    1
+  875      74   223    83   230    1
+  876     151   221   174   230    1
+  877     174   221   122   230    1
+  878     122   221   151   230    1
+  879     212   237   233   266    0
+  880     114   212   172   233    1
+  881     213   114   162   233    1
+  882     162   114   172   233    1
+  883     211   240    44   259    0
+  884     162   233   172   238    1
+  885     237   266    74   268    0
+  886       2     6     7   268    0
+  887     120   169   216   235    1
+  888     216   169   147   235    1
+  889      97   171   120   235    1
+  890      97   220   171   235    1
+  891     171   220   120   235    1
+  892     140   202    52   260    0
+  893     216   218   120   235    1
+  894     133   227    42   256    0
+  895      58   218   216   235    1
+  896     147   217    46   235    1
+  897     120   220   217   235    1
+  898     217   220    46   235    1
+  899     120   217   169   235    1
+  900     169   217   147   235    1
+  901       2   266   254   268    0
+  902     191   227   133   256    0
+  903       3   258     1   269    0
+  904     220   236   199   239    0
+  905     212   233   214   266    0
+  906     171   220    97   239    1
+  907      54   236   117   239    1
+  908     117   236   220   239    1
+  909     117   164    54   239    1
+  910     164   166    54   239    1
+  911     117   220   171   239    1
+  912      97   166    82   239    1
+  913      82   171    97   239    1
+  914      82   164   117   239    1
+  915      82   166   164   239    1
+  916     117   171    82   239    1
+  917      44   139   102   240    1
+  918      97   258   235   269    0
+  919     256   254   191   267    0
+  920     203   223   148   277    0
+  921       2   263     0   271    0
+  922     187   191    72   254    0
+  923      75   232    79   242    1
+  924     112   200   144   242    1
+  925     144   200    52   242    1
+  926     187   233    40   254    0
+  927      79   232   112   242    1
+  928     112   232   200   242    1
+  929     203   258   261   277    0
+  930     260   273   208   275    0
+  931     209   246   106   275    0
+  932     205   264   261   270    0
+  933       5   262   260   275    0
+  934     242   260   199   273    0
+  935      46   220   202   258    0
+  936     202   203    46   258    0
+  937      50   205   109   246    1
+  938      50   145   106   246    1
+  939     109   205   201   246    1
+  940     145   201    78   246    1
+  941     201   205    73   246    1
+  942      78   201    73   246    1
+  943     246   264   140   275    0
+  944     126   209    84   248    1
+  945     125   247   179   248    1
+  946     209   247    84   248    1
+  947     126   181   106   248    1
+  948      50   181   179   248    1
+  949     140   264   246   270    0
+  950     199   260   258   265    0
+  951     199   258   220   265    0
+  952     203   261   243   277    0
+  953     258   260     1   265    0
+  954     235   266   258   277    0
+  955      77   206   138   249    1
+  956      72   206    77   249    1
+  957     140   246    73   270    0
+  958     202   260    73   270    0
+  959     226   254   233   268    0
+  960      52   242   140   260    0
+  961      97   239   220   269    0
+  962      46   235   220   258    0
+  963     235   266   255   269    0
+  964     209   262    76   272    0
+  965      40   191   187   254    0
+  966     208   242    75   273    0
+  967      79   211    75   252    1
+  968      54   252   199   265    0
+  969      40   233   226   254    0
+  970     252   265   259   273    0
+  971     263   266     2   269    0
+  972       5   264     7   272    0
+  973      72   249   187   254    0
+  974     104   106   140   275    0
+  975      60   231   227   257    0
+  976     227   231    76   257    0
+  977     199    54   253   252    1
+  978     205   247   231   264    0
+  979     233   266   237   268    0
+  980       7     3     2   268    0
+  981     271   254   256   267    0
+  982     227   257    76   272    0
+  983     209   264   262   272    0
+  984     116   166   118   276    0
+  985     257   261   205   264    0
+  986     226   257   254   268    0
+  987     244   262   208   273    0
+  988      97   235   120   255    1
+  989      42   193   101   256    1
+  990     193   195    77   256    1
+  991      77   191   101   256    1
+  992     101   193    77   256    1
+  993     208   273   262   275    0
+  994     189   226    60   257    0
+  995     189   227   226   257    0
+  996      60   227   189   257    0
+  997     226   227   191   257    0
+  998       2   254     6   268    0
+  999     191   254   226   257    0
+ 1000      74   266   261   268    0
+ 1001       7   261     3   268    0
+ 1002     226   233    74   268    0
+ 1003     208   244   156   275    0
+ 1004     208   260   242   273    0
+ 1005      58   255   235   266    0
+ 1006     194   229   228   278    0
+ 1007     229   244   228   278    0
+ 1008     228   240   136   278    0
+ 1009     229   262   244   278    0
+ 1010     209   229    76   262    0
+ 1011     260   264   140   270    0
+ 1012     106   246   140   275    0
+ 1013     243   261   223   277    0
+ 1014     151   243   205   261    0
+ 1015      97   214   166   263    0
+ 1016       6   257     7   268    0
+ 1017      74   261   257   268    0
+ 1018      74   257   226   268    0
+ 1019       0   265     1   273    0
+ 1020      11   147   148   277    0
+ 1021       7     6     4   272    0
+ 1022       5   264   262   275    0
+ 1023     208   262   244   275    0
+ 1024      13   106   104   275    0
+ 1025     136   195   135   278    0
+ 1026       0     6     2   271    0
+ 1027      76   247   209   264    0
+ 1028     254   263     2   271    0
+ 1029       6   254     2   271    0
+ 1030     258   265     1   269    0
+ 1031       0   265   263   269    0
+ 1032     258   260   202   270    0
+ 1033     262   273   260   275    0
+ 1034     250   267   229   278    0
+ 1035     187   254   249   263    0
+ 1036       4     5     7   272    0
+ 1037     151   230    48   261    0
+ 1038      48   243   151   261    0
+ 1039     220   258    97   269    0
+ 1040     263   265   166   269    0
+ 1041     220   265   258   269    0
+ 1042     260   262     5   273    0
+ 1043       6   257   254   267    0
+ 1044     227   267   257   272    0
+ 1045     262   267     4   278    0
+ 1046       5     4     0   273    0
+ 1047       0   259     4   271    0
+ 1048     259   267     4   271    0
+ 1049     104   274    52   141    1
+ 1050     209   264   246   275    0
+ 1051     254   257     6   268    0
+ 1052       3   266     2   268    0
+ 1053     257   261     7   268    0
+ 1054     254   266   233   268    0
+ 1055       6   267     4   272    0
+ 1056     262   264     5   272    0
+ 1057     250   256   227   267    0
+ 1058     227   256   191   267    0
+ 1059     191   257   227   267    0
+ 1060       5   262     4   273    0
+ 1061     166   265   263   276    0
+ 1062     259   265   252   276    0
+ 1063     258   266     3   277    0
+ 1064     223   266   237   277    0
+ 1065     223   237   216   277    0
+ 1066     140   264   260   275    0
+ 1067     156   244   209   275    0
+ 1068     104   208   156   275    0
+ 1069     214   233   187   266    0
+ 1070     187   263   214   266    0
+ 1071     233   254   187   266    0
+ 1072     254   263   187   266    0
+ 1073     195   256   135   278    0
+ 1074     135   250   229   278    0
+ 1075     194   228   136   278    0
+ 1076     135   256   250   278    0
+ 1077     254   256   191    72    0
+ 1078     166   239    97   269    0
+ 1079     166   265   239   269    0
+ 1080      97   255   214   269    0
+ 1081     214   263    97   269    0
+ 1082     214   266   263   269    0
+ 1083     255   266   214   269    0
+ 1084     246   264   205   270    0
+ 1085     205   243    73   270    0
+ 1086     205   261   243   270    0
+ 1087     203   258    73   270    0
+ 1088     135   229   194   278    0
+ 1089      73   243   203   270    0
+ 1090     243   261   203   270    0
+ 1091     256   267   195   271    0
+ 1092      72   240   206   271    0
+ 1093     240   259   206   271    0
+ 1094     206   249    72   271    0
+ 1095     206   263   249   271    0
+ 1096     262   267   250   272    0
+ 1097      76   250   227   272    0
+ 1098     250   267   227   272    0
+ 1099     231   247    76   272    0
+ 1100     231   264   247   272    0
+ 1101     247   264    76   272    0
+ 1102     229   250    76   272    0
+ 1103     229   262   250   272    0
+ 1104      76   262   229   272    0
+ 1105     260   265   199   273    0
+ 1106      75   242   199   273    0
+ 1107      75   252   211   273    0
+ 1108     252   259   211   273    0
+ 1109     211   259    75   273    0
+ 1110     199   252    75   273    0
+ 1111     199   265   252   273    0
+ 1112     228   244    75   273    0
+ 1113     228   262   244   273    0
+ 1114      13   156   106   275    0
+ 1115     104   156    13   275    0
+ 1116     206   271   259   276    0
+ 1117     263   265     0   276    0
+ 1118       0   265   259   276    0
+ 1119      38   206   118   276    0
+ 1120      38   263   206   276    0
+ 1121     118   166    38   276    0
+ 1122     166   263    38   276    0
+ 1123       8   116   118   276    0
+ 1124       8   153   116   276    0
+ 1125     118   153     8   276    0
+ 1126       0   271   263   276    0
+ 1127     259   271     0   276    0
+ 1128      11   216   147   277    0
+ 1129     148   216    11   277    0
+ 1130     147   235    46   277    0
+ 1131     235   258    46   277    0
+ 1132      46   203   147   277    0
+ 1133      46   258   203   277    0
+ 1134     216   237    58   277    0
+ 1135     237   266    58   277    0
+ 1136       4   267   259   278    0
+ 1137     228   259   240   278    0
+ 1138     240   259   195   278    0
+ 1139     195   271   267   278    0
+ 1140     267   271   259   278    0
+ 1141     259   271   195   278    0
+ 1142     136    12   194   278    0
+ 1143     194    12   135   278    0
+ 1144     135    12   136   278    0
+# Generated by ../tetgen1.4.3/tetgen -pKAq2 box
diff --git a/example/tetgen/p8est_box_tetgen.node b/example/tetgen/p8est_box_tetgen.node
new file mode 100644
index 0000000..a7ce55d
--- /dev/null
+++ b/example/tetgen/p8est_box_tetgen.node
@@ -0,0 +1,281 @@
+279  3  0  0
+   0    0  0  0
+   1    1  0  0
+   2    0  1  0
+   3    1  1  0
+   4    0  0  1
+   5    1  0  1
+   6    0  1  1
+   7    1  1  1
+   8    -4  -4  -4
+   9    5  -4  -4
+  10    -4  5  -4
+  11    5  5  -4
+  12    -4  -4  5
+  13    5  -4  5
+  14    -4  5  5
+  15    5  5  5
+  16    -6  -6  -6
+  17    7  -6  -6
+  18    -6  7  -6
+  19    7  7  -6
+  20    -6  -6  7
+  21    7  -6  7
+  22    -6  7  7
+  23    7  7  7
+  24    -6  0.5  -6
+  25    -6  7  0.5
+  26    -6  0.5  7
+  27    -6  -6  0.5
+  28    7  0.5  -6
+  29    7  7  0.5
+  30    7  0.5  7
+  31    7  -6  0.5
+  32    0.5  -6  -6
+  33    0.5  -6  7
+  34    0.5  7  -6
+  35    0.5  7  7
+  36    -6  -2.75  -6
+  37    -6  3.75  -6
+  38    -4  0.5  -4
+  39    -6  7  3.75
+  40    -4  5  0.5
+  41    -6  -2.75  7
+  42    -4  0.5  5
+  43    -6  -6  -2.75
+  44    -4  -4  0.5
+  45    7  3.75  -6
+  46    5  0.5  -4
+  47    7  7  3.75
+  48    5  5  0.5
+  49    7  -2.75  7
+  50    5  0.5  5
+  51    7  -6  -2.75
+  52    5  -4  0.5
+  53    -2.75  -6  -6
+  54    0.5  -4  -4
+  55    3.75  -6  7
+  56    0.5  -4  5
+  57    3.75  7  -6
+  58    0.5  5  -4
+  59    -2.75  7  7
+  60    0.5  5  5
+  61    -6  7  -2.75
+  62    -6  3.75  7
+  63    -6  -6  3.75
+  64    7  -2.75  -6
+  65    7  7  -2.75
+  66    7  3.75  7
+  67    7  -6  3.75
+  68    3.75  -6  -6
+  69    -2.75  -6  7
+  70    -2.75  7  -6
+  71    3.75  7  7
+  72    -4  0.5  0.5
+  73    5  0.5  0.5
+  74    0.5  5  0.5
+  75    0.5  -4  0.5
+  76    0.5  0.5  5
+  77    -6  0.5  0.5
+  78    7  -1.125  2.125
+  79    0.5  -6  0.5
+  80    -6  7  -4.375
+  81    -4.375  7  -6
+  82    -1.125  -1.125  -6
+  83    0.5  7  0.5
+  84    0.5  0.5  7
+  85    5.375  -6  -6
+  86    7  -4.375  -6
+  87    5.375  7  7
+  88    7  5.375  7
+  89    -4.375  -6  7
+  90    -6  -6  5.375
+  91    -6  7  5.375
+  92    -6  5.375  7
+  93    -6  -6  -4.375
+  94    -6  -4.375  -6
+  95    7  5.375  -6
+  96    7  7  -4.375
+  97    0.5  0.5  -4
+  98    7  -4.375  7
+  99    7  -6  5.375
+ 100    -6  3.75  -1.125
+ 101    -6  2.125  3.75
+ 102    -6  -2.75  2.125
+ 103    -6  -2.2083333333333335  -2.2083333333333335
+ 104    5  -4  2.75
+ 105    7  -2.75  -1.125
+ 106    5  -1.75  5
+ 107    7  -3.15625  4.15625
+ 108    7  2.4500000000000002  -1.125
+ 109    7  2.125  3.75
+ 110    -1.125  -6  -2.75
+ 111    2.125  -6  3.75
+ 112    3.2083333333333335  -6  -2.2083333333333335
+ 113    -2.2083333333333335  -6  3.2083333333333335
+ 114    -3.5625  7  -3.5625
+ 115    -3.5625  4.5625  -6
+ 116    -1.75  -4  -4
+ 117    2.125  -2.75  -6
+ 118    -4  -1.75  -4
+ 119    -3.15625  -3.15625  -6
+ 120    2.125  2.4500000000000002  -6
+ 121    2.125  7  -2.75
+ 122    2.125  7  3.75
+ 123    -2.2083333333333335  7  3.2083333333333335
+ 124    -1.125  -2.75  7
+ 125    2.125  3.75  7
+ 126    3.2083333333333335  -2.2083333333333335  7
+ 127    -2.2083333333333335  3.2083333333333335  7
+ 128    7  4.5625  -3.5625
+ 129    -4  2.75  -4
+ 130    -6  4.5625  -3.5625
+ 131    -6  4.5625  4.5625
+ 132    -4  5  2.75
+ 133    -4  2.75  5
+ 134    -6  -3.5625  4.5625
+ 135    -4  -1.75  5
+ 136    -4  -4  2.75
+ 137    -6  -3.9494047619047619  -3.5625
+ 138    -6  0.97629310344827491  -2.7966954022988499
+ 139    -6  -3.6262254901960782  -0.18504901960784348
+ 140    5  -1.75  1.625
+ 141    7  -3.5625  1.3124999999999998
+ 142    7  -3.5625  -3.5625
+ 143    5  -1.75  -4
+ 144    5  -4  -1.75
+ 145    7  -0.6171875  4.6640625
+ 146    7  -0.14999999999999991  -0.39374999999999982
+ 147    5  2.75  -4
+ 148    5  5  -1.75
+ 149    7  4.1014534883720932  1.4334302325581394
+ 150    7  4.5625  4.5625
+ 151    5  5  2.75
+ 152    5  2.75  5
+ 153    -4  -4  -1.75
+ 154    -3.5625  -6  -3.5625
+ 155    4.5625  -6  2.125
+ 156    2.75  -4  5
+ 157    4.5625  -6  -3.9494047619047619
+ 158    1.1850490196078431  -6  -3.6262254901960782
+ 159    -3.5625  -6  4.9494047619047619
+ 160    -2.7966954022988508  -6  0.023706896551724199
+ 161    -0.18504901960784315  -6  4.6262254901960782
+ 162    -2.75  7  -0.3125
+ 163    -2.9756944444444446  1.4479166666666665  -6
+ 164    -0.3125  -3.5625  -6
+ 165    4.5625  -3.5625  -6
+ 166    -1.375  -1.375  -4
+ 167    -6  -1.125  -4.2976190476190474
+ 168    -4.3169642857142856  -1.125  -6
+ 169    4.2546052631578952  4.2546052631578952  -6
+ 170    -0.50749999999999984  4.0749999999999993  -6
+ 171    1.3937500000000003  -0.14999999999999991  -6
+ 172    -0.6875  7  -3.375
+ 173    4.5625  7  -3.5625
+ 174    3.75  7  0.09375
+ 175    4.5625  7  4.5625
+ 176    -3.7430555555555554  7  4.9236111111111116
+ 177    -0.18504901960784403  7  4.6262254901960791
+ 178    -3.5625  -1.125  7
+ 179    4.5625  2.125  7
+ 180    4.7430555555555554  -3.9236111111111112  7
+ 181    5.2976190476190474  -1.125  7
+ 182    1.1850490196078431  -3.6262254901960782  7
+ 183    -3.7430555555555554  4.9236111111111107  7
+ 184    -0.18504901960784315  4.6262254901960782  7
+ 185    7  4.9909090909090912  -1.0570454545454548
+ 186    7  1.9026315789473682  -3.7335526315789473
+ 187    -4  2.75  -1.1875
+ 188    -6  4.15625  1.71875
+ 189    -1.75  5  5
+ 190    -4.2976190476190474  7  2.125
+ 191    -4  2.375  2.375
+ 192    -4.2976190476190474  2.125  7
+ 193    -6  -0.71875  4.1562499999999991
+ 194    -1.75  -4  5
+ 195    -4  -1.375  2.375
+ 196    -4.2976190476190474  -6  2.125
+ 197    -6  2.5422616386296313  -4.24325387954321
+ 198    7  -1.125  -4.104166666666667
+ 199    2.1875  -4  -1.75
+ 200    5.2976190476190474  -6  -1.125
+ 201    7  1.1834881756756757  1.5705236486486487
+ 202    5  -0.625  -1.75
+ 203    5  2.375  -1.375
+ 204    2.75  5  5
+ 205    5  2.375  2.375
+ 206    -4  -1.375  -1.375
+ 207    4.6979166666666661  -6  4.5625
+ 208    2.375  -4  2.375
+ 209    2.375  -1.375  5
+ 210    -4.2432538795432109  -6  -1.5422616386296317
+ 211    -1.75  -4  -0.625
+ 212    -1.75  5  -4
+ 213    -4  5  -1.75
+ 214    -1.75  1.625  -4
+ 215    5.104166666666667  -1.125  -6
+ 216    2.75  5  -4
+ 217    4.6324410309418109  1.6498525773545265  -6
+ 218    1.824374999999999  4.9078125000000004  -6
+ 219    -0.58336330603511877  1.216657290590724  -6
+ 220    2.75  -0.625  -4
+ 221    5.146484375  7  2.125
+ 222    5.227909482758621  7  -1.4962284482758619
+ 223    2.75  5  -0.625
+ 224    -3.6640625  -3.61328125  7
+ 225    4.6979166666666661  4.5625  7
+ 226    -1.375  5  2.375
+ 227    -1.375  2.375  5
+ 228    -1.375  -4  2.375
+ 229    -0.625  -1.75  5
+ 230    1.625  5  2.75
+ 231    1.625  2.75  5
+ 232    2.4296875  -6  1.56640625
+ 233    -1.75  5  -0.625
+ 234    -4.28125  7  -1.6562499999999998
+ 235    2.375  2.375  -4
+ 236    2.75  -4  -4
+ 237    0.21875  5  -1.75
+ 238    0.390625  7  -1.5859374999999998
+ 239    0.8125  -1.6875  -4
+ 240    -4  -2.0178571428571428  0.5
+ 241    -6  -1.5978616084114807  0.36677678317703888
+ 242    2.5758928571428568  -4  0.29910714285714279
+ 243    5  3.0178571428571428  0.5
+ 244    0.5  -4  3.0178571428571428
+ 245    0.36677678317703921  -6  2.5978616084114803
+ 246    5  0.1875  2.6875
+ 247    2.6875  0.8125  5
+ 248    3.3185263746382527  0.13548654959221285  7
+ 249    -4  0.70089285714285721  -1.5758928571428568
+ 250    -1.6875  0.1875  5
+ 251    -2.3185263746382527  0.86451345040778715  7
+ 252    0.11647727272727276  -4  -1.5454545454545454
+ 253    0.63322321682296079  -6  -1.5978616084114803
+ 254    -2.1572265625  2.2578125  0.5
+ 255    0.1875  2.6875  -4
+ 256    -4  0.5  3.0178571428571428
+ 257    0.50000000000000111  2.745923913043478  2.745923913043478
+ 258    2.745923913043478  0.49999999999999889  -1.745923913043478
+ 259    -1.5547750115083625  -1.8295515574650913  0.5
+ 260    2.8249762958280655  -1.5652327523929928  0.50000000000000044
+ 261    2.5547750115083625  2.8295515574650913  0.5
+ 262    0.5  -1.5547750115083625  2.8295515574650909
+ 263    -1.5652327523929928  0.5  -1.8249762958280655
+ 264    2.6435439560439562  0.50000000000000089  2.6435439560439558
+ 265    0.50000000000000044  -1.6520748158290584  -1.6399449995721163
+ 266    0.5  2.6421407185628745  -1.6441359468562875
+ 267    -1.649978637888474  0.5  2.6408293246407997
+ 268    -0.20619746340980982  3.0257746829262264  0.5
+ 269    0.5  0.5  -1.9375
+ 270    2.9375  0.5  0.5
+ 271    -1.9725739554073036  0.21940835674157322  0.5
+ 272    0.50000000000000022  0.71051429538149202  2.9638142869226867
+ 273    0.5  -1.9319923564048069  0.5
+ 274    7  -6  2.125
+ 275    2.8119967662925922  -1.7844895388422679  2.7844895388422684
+ 276    -1.7859633504304404  -1.8313227626343682  -1.7859633504304413
+ 277    2.7852382099874244  2.8244307614726969  -1.7852382099874242
+ 278    -1.7855722933290226  -1.7855722933290221  2.8276060122817799
+# Generated by ../tetgen1.4.3/tetgen -pKAq2 box
diff --git a/example/tetgen/read_conn2.c b/example/tetgen/read_conn2.c
new file mode 100644
index 0000000..e6d3108
--- /dev/null
+++ b/example/tetgen/read_conn2.c
@@ -0,0 +1,60 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_connectivity.h>
+#else
+#include <p8est_connectivity.h>
+#endif
+
+int
+main (int argc, char **argv)
+{
+  p4est_connectivity_t *conn;
+  size_t              bytes;
+
+  if (argc != 2) {
+    char               *cp, *bn;
+
+    cp = strdup (argv[0]);
+    bn = basename (cp);
+    fprintf (stderr, "Usage: %s <connectivity file>\n", bn);
+    free (cp);
+    exit (1);
+  }
+
+  conn = p4est_connectivity_load (argv[1], &bytes);
+  if (conn == NULL) {
+    P4EST_PRODUCTIONF ("Failed to load connectivity file \"%s\"\n", argv[1]);
+  }
+  else {
+    P4EST_STATISTICSF ("Loaded \"%s\" bytes %lld\n",
+                       argv[1], (long long) bytes);
+    P4EST_STATISTICSF ("Loaded %lld trees, %lld vertices, %lld corners\n",
+                       (long long) conn->num_trees,
+                       (long long) conn->num_vertices,
+                       (long long) conn->num_corners);
+    p4est_connectivity_destroy (conn);
+  }
+
+  return 0;
+}
diff --git a/example/tetgen/read_conn3.c b/example/tetgen/read_conn3.c
new file mode 100644
index 0000000..ae456a4
--- /dev/null
+++ b/example/tetgen/read_conn3.c
@@ -0,0 +1,24 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "read_conn2.c"
diff --git a/example/tetgen/read_tetgen.c b/example/tetgen/read_tetgen.c
new file mode 100644
index 0000000..c797e76
--- /dev/null
+++ b/example/tetgen/read_tetgen.c
@@ -0,0 +1,90 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p8est_tets_hexes.h>
+#include <p8est_vtk.h>
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret, retval;
+  int                 mpirank;
+  const char         *argbasename;
+  char                afilename[BUFSIZ];
+  p4est_topidx_t      tnum_flips;
+  p8est_tets_t       *ptg;
+  p8est_connectivity_t *connectivity;
+  p8est_t            *p8est;
+  sc_MPI_Comm         mpicomm;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  if (argc != 2) {
+    SC_GLOBAL_LERRORF ("Usage: %s <tetgen file base name>\n", argv[0]);
+    sc_abort ();
+  }
+  argbasename = argv[1];
+
+  /* read tetgen nodes and tetrahedra from files */
+  ptg = p8est_tets_read (argbasename);
+  SC_CHECK_ABORTF (ptg != NULL, "Failed to read tetgen %s", argbasename);
+  P4EST_GLOBAL_STATISTICSF ("Read %d nodes and %d tets %s attributes\n",
+                            (int) ptg->nodes->elem_count / 3,
+                            (int) ptg->tets->elem_count / 4,
+                            ptg->tet_attributes != NULL ? "with" : "without");
+
+  /* flip orientation to right-handed */
+  tnum_flips = p8est_tets_make_righthanded (ptg);
+  P4EST_GLOBAL_STATISTICSF ("Performed %ld orientation flip(s)\n",
+                            (long) tnum_flips);
+
+  /* create a connectivity from the tet mesh and save it */
+  connectivity = p8est_connectivity_new_tets (ptg);
+  if (mpirank == 0) {
+    snprintf (afilename, BUFSIZ, "%s", "read_tetgen.p8c");
+    retval = p8est_connectivity_save (afilename, connectivity);
+    SC_CHECK_ABORT (retval == 0, "Failed connectivity_save");
+  }
+
+  /* create a forest and visualize */
+  p8est = p8est_new (mpicomm, connectivity, 0, NULL, NULL);
+  snprintf (afilename, BUFSIZ, "%s", "read_tetgen");
+  p8est_vtk_write_file (p8est, NULL, afilename);
+
+  /* clean up */
+  p8est_destroy (p8est);
+  p8est_connectivity_destroy (connectivity);
+  p8est_tets_destroy (ptg);
+
+  sc_finalize ();
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/tetgen/write_conn2.c b/example/tetgen/write_conn2.c
new file mode 100644
index 0000000..63b791d
--- /dev/null
+++ b/example/tetgen/write_conn2.c
@@ -0,0 +1,64 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/* This program is NOT collective (i.e., usually NOT called with mpirun). */
+
+#ifndef P4_TO_P8
+#include <p4est_connectivity.h>
+#else
+#include <p8est_connectivity.h>
+#endif
+
+int
+main (int argc, char **argv)
+{
+  p4est_connectivity_t *conn;
+  int                 retval;
+  char                buf[BUFSIZ];
+
+  if (argc != 2) {
+    char               *cp, *bn;
+
+    cp = strdup (argv[0]);
+    bn = basename (cp);
+    fprintf (stderr, "Usage: %s <connectivity name>\n", bn);
+    free (cp);
+    exit (1);
+  }
+
+  conn = p4est_connectivity_new_byname (argv[1]);
+  if (conn == NULL) {
+    P4EST_PRODUCTIONF ("Failed to identify connectivity \"%s\"\n", argv[1]);
+  }
+  else {
+    snprintf (buf, BUFSIZ, "%s_%s.p%dc",
+              P4EST_STRING, argv[1], P4EST_CHILDREN);
+    P4EST_INFOF ("Write connectivity file \"%s\"\n", buf);
+    retval = p4est_connectivity_save (buf, conn);
+    if (retval) {
+      P4EST_PRODUCTION ("Error writing connectivity file\n");
+    }
+    p4est_connectivity_destroy (conn);
+  }
+
+  return 0;
+}
diff --git a/example/tetgen/write_conn3.c b/example/tetgen/write_conn3.c
new file mode 100644
index 0000000..62cef6c
--- /dev/null
+++ b/example/tetgen/write_conn3.c
@@ -0,0 +1,26 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/* This program is NOT collective (i.e., usually NOT called with mpirun). */
+
+#include <p4est_to_p8est.h>
+#include "write_conn2.c"
diff --git a/example/timings/Makefile.am b/example/timings/Makefile.am
new file mode 100644
index 0000000..2bf9976
--- /dev/null
+++ b/example/timings/Makefile.am
@@ -0,0 +1,41 @@
+
+# This file is part of p4est.
+# Makefile.am in example/timings
+# included non-recursively from toplevel directory
+
+if P4EST_ENABLE_BUILD_2D
+bin_PROGRAMS += \
+        example/timings/p4est_timings \
+        example/timings/p4est_bricks \
+        example/timings/p4est_loadconn
+
+example_timings_p4est_timings_SOURCES = example/timings/timings2.c
+example_timings_p4est_bricks_SOURCES = example/timings/bricks2.c
+example_timings_p4est_loadconn_SOURCES = example/timings/loadconn2.c
+
+LINT_CSOURCES += \
+        $(example_timings_p4est_timings_SOURCES) \
+        $(example_timings_p4est_bricks_SOURCES) \
+        $(example_timings_p4est_loadconn_SOURCES)
+endif
+
+if P4EST_ENABLE_BUILD_3D
+bin_PROGRAMS += \
+        example/timings/p8est_timings \
+        example/timings/p8est_bricks \
+        example/timings/p8est_loadconn \
+        example/timings/p8est_tsearch
+
+example_timings_p8est_timings_SOURCES = example/timings/timings3.c
+example_timings_p8est_bricks_SOURCES = example/timings/bricks3.c
+example_timings_p8est_loadconn_SOURCES = example/timings/loadconn3.c
+example_timings_p8est_tsearch_SOURCES = example/timings/tsearch3.c
+
+LINT_CSOURCES += \
+        $(example_timings_p8est_timings_SOURCES) \
+        $(example_timings_p8est_bricks_SOURCES) \
+        $(example_timings_p8est_loadconn_SOURCES) \
+        $(example_timings_p8est_tsearch_SOURCES)
+endif
+
+EXTRA_DIST += example/timings/timana.awk example/timings/timana.sh
diff --git a/example/timings/bricks2.c b/example/timings/bricks2.c
new file mode 100644
index 0000000..438bdec
--- /dev/null
+++ b/example/timings/bricks2.c
@@ -0,0 +1,171 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_vtk.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_vtk.h>
+#endif
+#include <sc_options.h>
+
+/* #define BRICKS_VTK */
+
+static int          refine_level;
+static int          level_shift;
+
+static int
+refine_fractal (p4est_t * p4est, p4est_topidx_t which_tree,
+                p4est_quadrant_t * q)
+{
+  int                 qid;
+
+  if ((int) q->level >= refine_level) {
+    return 0;
+  }
+  if ((int) q->level < refine_level - level_shift) {
+    return 1;
+  }
+
+  qid = ((int) q->level == 0 ?
+         (which_tree % P4EST_CHILDREN) : p4est_quadrant_child_id (q));
+
+  return (qid == 0 || qid == 3
+#ifdef P4_TO_P8
+          || qid == 5 || qid == 6
+#endif
+    );
+}
+
+static void
+run_bricks (sc_MPI_Comm mpicomm, int per, int l, int rlevel)
+{
+  int                 mpiret;
+  int                 tcount;
+  double              elapsed_create, elapsed_partition, elapsed_balance;
+#ifdef BRICKS_VTK
+  char                filename[BUFSIZ];
+#endif
+  p4est_connectivity_t *conn;
+  p4est_t            *p4est;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Run bricks on level %d/%d\n", l, rlevel);
+  P4EST_ASSERT (l <= rlevel);
+
+  /* create and refine the forest */
+
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_create = -sc_MPI_Wtime ();
+
+  tcount = 1 << l;
+#ifndef P4_TO_P8
+  conn = p4est_connectivity_new_brick (tcount, tcount, per, per);
+#else
+  conn = p8est_connectivity_new_brick (tcount, tcount, tcount, per, per, per);
+#endif
+  p4est = p4est_new_ext (mpicomm, conn, 0, rlevel - l, 1, 0, NULL, NULL);
+
+  level_shift = 4;
+  refine_level = rlevel - l + level_shift;
+  p4est_refine (p4est, 1, refine_fractal, NULL);
+
+  elapsed_create += sc_MPI_Wtime ();
+
+  /* partition the forest */
+
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_partition = -sc_MPI_Wtime ();
+
+  p4est_partition (p4est, 0, NULL);
+
+  elapsed_partition += sc_MPI_Wtime ();
+
+  /* balance the forest */
+
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_balance = -sc_MPI_Wtime ();
+
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+
+  elapsed_balance += sc_MPI_Wtime ();
+
+  /* postprocessing */
+
+  P4EST_GLOBAL_PRODUCTIONF ("Timings %g %g %g\n", elapsed_create,
+                            elapsed_partition, elapsed_balance);
+
+#ifdef BRICKS_VTK
+  snprintf (filename, BUFSIZ, "brick_%02d_%02d_B", rlevel, l);
+  p4est_vtk_write_file (p4est, NULL, filename);
+#endif
+
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (conn);
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret, retval;
+  int                 rlevel, l;
+  int                 periodic;
+  sc_options_t       *opt;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  opt = sc_options_new (argv[0]);
+  sc_options_add_int (opt, 'l', "level", &rlevel, 0,
+                      "Upfront refinement level");
+  sc_options_add_switch (opt, 'p', "periodic", &periodic,
+                         "Periodic connectivity");
+  retval = sc_options_parse (p4est_package_id, SC_LP_ERROR, opt, argc, argv);
+  if (retval == -1 || retval < argc) {
+    sc_options_print_usage (p4est_package_id, SC_LP_PRODUCTION, opt, NULL);
+    sc_abort_collective ("Usage error");
+  }
+
+  for (l = 0; l <= rlevel; ++l) {
+    run_bricks (mpicomm, periodic, l, rlevel);
+  }
+
+  sc_options_destroy (opt);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/timings/bricks3.c b/example/timings/bricks3.c
new file mode 100644
index 0000000..9e3e1e5
--- /dev/null
+++ b/example/timings/bricks3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "bricks2.c"
diff --git a/example/timings/loadconn2.c b/example/timings/loadconn2.c
new file mode 100644
index 0000000..11be191
--- /dev/null
+++ b/example/timings/loadconn2.c
@@ -0,0 +1,167 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_vtk.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_vtk.h>
+#endif
+#include <sc_options.h>
+
+/* #define LOADCONN_VTK */
+
+static int          refine_level;
+static int          level_shift;
+
+static int
+refine_fractal (p4est_t * p4est, p4est_topidx_t which_tree,
+                p4est_quadrant_t * q)
+{
+  int                 qid;
+
+  if ((int) q->level >= refine_level) {
+    return 0;
+  }
+  if ((int) q->level < refine_level - level_shift) {
+    return 1;
+  }
+
+  qid = ((int) q->level == 0 ?
+         (which_tree % P4EST_CHILDREN) : p4est_quadrant_child_id (q));
+
+  return (qid == 0 || qid == 3
+#ifdef P4_TO_P8
+          || qid == 5 || qid == 6
+#endif
+    );
+}
+
+static void
+run_load (sc_MPI_Comm mpicomm, p4est_connectivity_t * conn, int level)
+{
+  int                 mpiret;
+  double              elapsed_create, elapsed_partition, elapsed_balance;
+#ifdef LOADCONN_VTK
+  char                filename[BUFSIZ];
+#endif
+  p4est_t            *p4est;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Run load on level %d\n", level);
+
+  /* create and refine the forest */
+
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_create = -sc_MPI_Wtime ();
+
+  p4est = p4est_new_ext (mpicomm, conn, 0, level, 1, 0, NULL, NULL);
+
+  level_shift = 4;
+  refine_level = level + level_shift;
+  p4est_refine (p4est, 1, refine_fractal, NULL);
+
+  elapsed_create += sc_MPI_Wtime ();
+
+#ifdef LOADCONN_VTK
+  snprintf (filename, BUFSIZ, "loadconn%d_%02d_C", P4EST_DIM, level);
+  p4est_vtk_write_file (p4est, NULL, filename);
+#endif
+
+  /* partition the forest */
+
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_partition = -sc_MPI_Wtime ();
+
+  p4est_partition (p4est, 0, NULL);
+
+  elapsed_partition += sc_MPI_Wtime ();
+
+  /* balance the forest */
+
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_balance = -sc_MPI_Wtime ();
+
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+
+  elapsed_balance += sc_MPI_Wtime ();
+
+#ifdef LOADCONN_VTK
+  snprintf (filename, BUFSIZ, "loadconn%d_%02d_B", P4EST_DIM, level);
+  p4est_vtk_write_file (p4est, NULL, filename);
+#endif
+
+  /* report timings */
+
+  P4EST_GLOBAL_PRODUCTIONF ("Timings %d: %g %g %g\n", level, elapsed_create,
+                            elapsed_partition, elapsed_balance);
+
+  p4est_destroy (p4est);
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret, retval;
+  int                 level;
+  const char         *filename;
+  p4est_connectivity_t *conn;
+  sc_options_t       *opt;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  opt = sc_options_new (argv[0]);
+  sc_options_add_int (opt, 'l', "level", &level, 0,
+                      "Upfront refinement level");
+  retval = sc_options_parse (p4est_package_id, SC_LP_ERROR, opt, argc, argv);
+  if (retval == -1 || retval + 1 != argc) {
+    sc_options_print_usage (p4est_package_id, SC_LP_PRODUCTION, opt, NULL);
+    sc_abort_collective ("Usage error");
+  }
+  filename = argv[retval];
+  P4EST_LDEBUGF ("Loading %s\n", filename);
+  conn = p4est_connectivity_load (filename, NULL);
+
+  run_load (mpicomm, conn, level);
+
+  p4est_connectivity_destroy (conn);
+  sc_options_destroy (opt);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/timings/loadconn3.c b/example/timings/loadconn3.c
new file mode 100644
index 0000000..50e643f
--- /dev/null
+++ b/example/timings/loadconn3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "loadconn2.c"
diff --git a/example/timings/perfscript.sh b/example/timings/perfscript.sh
new file mode 100755
index 0000000..04dfc23
--- /dev/null
+++ b/example/timings/perfscript.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+command -v perf >/dev/null 2>&1 || { echo >&2 "Could not find perf.  Aborting."; exit 1; }
+
+STATS="instructions,cache-misses,branch-misses"
+REPCOUNT=30
+
+SPACESTATS=`echo $STATS | sed 's/,/ /g'`
+echo "type level time $SPACESTATS" > p8.perf
+
+for i in 5 6 7 8; do
+  for type in "base" "nodes" "lnodes"; do
+    if [ $type = "nodes" ]; then
+      othertype="lnodes"
+    elif [ $type = "base" ]; then
+      othertype="lnodes --skip-nodes"
+    else
+      othertype="nodes"
+    fi
+    perf stat -o p8.$type.$i -e "$STATS" -r $REPCOUNT ./p8est_timings \
+      -c unit --skip-$othertype --level $i > p8.$type.$i.stdout
+    runtime=`egrep "[0-9.]+ seconds time elapsed" p8.$type.$i | awk '{print $1;}'`
+    accum=$runtime
+    for stat in $SPACESTATS; do
+      count=`egrep "[0-9,]+ $stat" p8.$type.$i | awk '{print $1;}' | sed 's/,//g'`
+      accum="$accum $count"
+    done
+    if [ $type = "base" ]; then
+      baseaccum=$accum
+    else
+      read -a basearray <<< "$baseaccum"
+      read -a array <<< "$accum"
+      for j in "${!array[@]}"; do
+        val=`echo "${array[j]}-${basearray[j]}" | bc -l`
+        array[j]=$val
+      done
+      diff="${array[*]}"
+      echo "$type $i $diff" >> p8.perf
+    fi
+  done
+done
+
diff --git a/example/timings/timana.awk b/example/timings/timana.awk
new file mode 100755
index 0000000..fe171ad
--- /dev/null
+++ b/example/timings/timana.awk
@@ -0,0 +1,21 @@
+#! /usr/bin/awk -f
+
+/New p(4|8)est/ {
+	if ($3 == "p4est") { dim = 2 }
+	else if ($3 == "p8est") { dim = 3 }
+	#print "We are in dimension " dim
+}
+/^\[p4est\] Processors [[:digit:]]+ level/ {
+	procs = $3
+	level = $5
+	elems = 0
+	#printf "Procs %d level %d\n", procs, level
+}
+/^\[p4est\] .* balanced to/ {
+	elems = $9
+}
+/^\[p4est\] Summary =/ {
+	# Output runtimes for: Refine Balance Partition Ghost Nodes Lnodes
+	printf "%d %d %d %g %g %g %g %g %g\n", procs, level, elems,
+		$5, $6, $33, $34, $35, $38
+}
diff --git a/example/timings/timana.sh b/example/timings/timana.sh
new file mode 100755
index 0000000..72b1526
--- /dev/null
+++ b/example/timings/timana.sh
@@ -0,0 +1,68 @@
+#! /bin/sh
+
+# Pipe in the contents of one or more log files written by p{4,8}est_timings
+# Optionally let the first and second argument be --title "Title of plot"
+
+# Find path for the content filter script
+TIMANAWK=`echo $0 | sed -e 's/\.sh$/.awk/'`
+if test ! -x "$TIMANAWK" ; then
+	echo "No executable $TIMANAWK"
+	exit 1
+fi
+
+TITLECOMM=
+if test "x$1" = "x--title" ; then
+	TITLECOMM="set title \"$2\""
+	shift
+	shift
+fi
+
+TMPF=`tempfile --prefix=timana`
+#echo "Temp file: $TMPF"
+
+$TIMANAWK $@ > $TMPF
+
+gnuplot <<EOF
+
+set logscale xy
+set key left
+set style data points
+
+#set output "timana.eps"
+set term postscript color
+$TITLECOMM
+
+# Fields 4..9: Refine Balance Partition Ghost Nodes Lnodes
+
+set xlabel "\#elements / core"
+set ylabel "runtime [1s]"
+
+set output "timana-balance.eps"
+plot "$TMPF" using (\$3 / \$1):(\$5) title "Balance" pt 5, \
+	5e-6 * x title "linear scaling" with lines, \
+	8e-4 * x**(2./3.) title "scaling with x**(2/3)" with lines, \
+	1 title "1 second"
+
+set output "timana-ghost.eps"
+plot "$TMPF" using (\$3 / \$1):(\$7) title "Ghost" pt 5, \
+	2e-4 * x**(2./3.) title "scaling with x**(2/3)" with lines, \
+	1 title "1 second"
+
+set output "timana-nodes.eps"
+plot "$TMPF" using (\$3 / \$1):(\$8) title "Nodes" pt 5, \
+	2e-5 * x title "linear scaling" with lines, \
+	1 title "1 second"
+
+set output "timana-lnodes.eps"
+plot "$TMPF" using (\$3 / \$1):(\$9) title "Lnodes" pt 5, \
+	2e-5 * x title "linear scaling" with lines, \
+	1 title "1 second"
+
+# set xlabel "Elements"
+# set ylabel "Runtime / \#cores [1s]"
+# plot "$TMPF" using (\$3):(\$5 / (\$3 * 1e-6 / \$1)) title "Balance"
+# plot "$TMPF" using (\$3):(\$7 / (\$3 * 1e-6 / \$1)) title "Ghost"
+# plot "$TMPF" using (\$3):(\$8 / (\$3 * 1e-6 / \$1)) title "Nodes"
+# plot "$TMPF" using (\$3):(\$9 / (\$3 * 1e-6 / \$1)) title "Lnodes"
+
+EOF
diff --git a/example/timings/timings2.c b/example/timings/timings2.c
new file mode 100644
index 0000000..6814850
--- /dev/null
+++ b/example/timings/timings2.c
@@ -0,0 +1,734 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * Usage: p4est_timings <configuration> <level>
+ *        possible configurations:
+ *        o unit      Refinement on the unit square.
+ *        o periodic  Refinement on the unit square with periodic b.c.
+ *        o three     Refinement on a forest with three trees.
+ *        o moebius   Refinement on a 5-tree Moebius band.
+ *        o star      Refinement on a 6-tree star shaped domain.
+ *
+ * Usage: p8est_timings <configuration> <level>
+ *        possible configurations:
+ *        o unit      Refinement on the unit cube.
+ *        o periodic  Refinement on the unit cube with all-periodic b.c.
+ *        o rotwrap   Refinement on the unit cube with weird periodic b.c.
+ *        o twocubes  Refinement on a forest with two trees.
+ *        o rotcubes  Refinement on a forest with six rotated trees.
+ *        o shell     Refinement on a 24-tree spherical shell.
+ */
+
+#ifndef P4_TO_P8
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_ghost.h>
+#include <p4est_nodes.h>
+#include <p4est_vtk.h>
+#include <p4est_lnodes.h>
+#else
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_ghost.h>
+#include <p8est_nodes.h>
+#include <p8est_vtk.h>
+#include <p8est_lnodes.h>
+#endif
+#include <sc_flops.h>
+#include <sc_statistics.h>
+#include <sc_options.h>
+
+/* #define P4EST_TIMINGS_VTK */
+
+typedef enum
+{
+  P4EST_CONFIG_NULL,
+  P4EST_CONFIG_UNIT,
+  P4EST_CONFIG_PERIODIC,
+#ifndef P4_TO_P8
+  P4EST_CONFIG_THREE,
+  P4EST_CONFIG_MOEBIUS,
+  P4EST_CONFIG_STAR
+#else
+  P4EST_CONFIG_ROTWRAP,
+  P4EST_CONFIG_TWOCUBES,
+  P4EST_CONFIG_ROTCUBES,
+  P4EST_CONFIG_SHELL
+#endif
+}
+timings_config_t;
+
+enum
+{
+  TIMINGS_REFINE,
+  TIMINGS_BALANCE,
+  TIMINGS_BALANCE_A,
+  TIMINGS_BALANCE_COMM,
+  TIMINGS_BALANCE_B,
+  TIMINGS_BALANCE_A_COUNT_IN,
+  TIMINGS_BALANCE_A_COUNT_OUT,
+  TIMINGS_BALANCE_COMM_SENT,
+  TIMINGS_BALANCE_COMM_NZPEERS,
+  TIMINGS_BALANCE_B_COUNT_IN,
+  TIMINGS_BALANCE_B_COUNT_OUT,
+  TIMINGS_BALANCE_RANGES,
+  TIMINGS_BALANCE_NOTIFY,
+  TIMINGS_BALANCE_NOTIFY_ALLGATHER,
+  TIMINGS_BALANCE_A_ZERO_SENDS,
+  TIMINGS_BALANCE_A_ZERO_RECEIVES,
+  TIMINGS_BALANCE_B_ZERO_SENDS,
+  TIMINGS_BALANCE_B_ZERO_RECEIVES,
+  TIMINGS_REBALANCE,
+  TIMINGS_REBALANCE_A,
+  TIMINGS_REBALANCE_COMM,
+  TIMINGS_REBALANCE_B,
+  TIMINGS_REBALANCE_A_COUNT_IN,
+  TIMINGS_REBALANCE_A_COUNT_OUT,
+  TIMINGS_REBALANCE_COMM_SENT,
+  TIMINGS_REBALANCE_COMM_NZPEERS,
+  TIMINGS_REBALANCE_B_COUNT_IN,
+  TIMINGS_REBALANCE_B_COUNT_OUT,
+  TIMINGS_PARTITION,
+  TIMINGS_GHOSTS,
+  TIMINGS_NODES,
+  TIMINGS_TRILINEAR_OBSOLETE,
+  TIMINGS_REPARTITION,
+  TIMINGS_LNODES,
+  TIMINGS_LNODES3,
+  TIMINGS_LNODES7,
+  TIMINGS_NUM_STATS
+};
+
+typedef struct
+{
+  timings_config_t    config;
+  int                 mpisize;
+  int                 level;
+  unsigned            checksum;
+}
+timings_regression_t;
+
+typedef struct
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpisize;
+  int                 mpirank;
+}
+mpi_context_t;
+
+static int          refine_level = 0;
+static int          level_shift = 0;
+
+/* *INDENT-OFF* */
+static const timings_regression_t regression_oldschool[] =
+{
+#ifndef P4_TO_P8
+  { P4EST_CONFIG_UNIT, 1, 10, 0x6e3e83c4U },
+  { P4EST_CONFIG_UNIT, 1, 11, 0x334bc3deU },
+  { P4EST_CONFIG_UNIT, 64, 14, 0xad908ce4U },
+  { P4EST_CONFIG_UNIT, 256, 15, 0x9e7da646U },
+  { P4EST_CONFIG_STAR, 1, 6, 0x14107b57U },
+  { P4EST_CONFIG_STAR, 4, 6, 0x14107b57U },
+  { P4EST_CONFIG_STAR, 52, 13, 0xc86c74d9U },
+  { P4EST_CONFIG_STAR, 64, 13, 0xc86c74d9U },
+#else
+  { P4EST_CONFIG_UNIT, 1, 5, 0xe1ffa67bU },
+  { P4EST_CONFIG_UNIT, 1, 6, 0x2cad814dU },
+  { P4EST_CONFIG_UNIT, 3, 8, 0xeb252238U },
+  { P4EST_CONFIG_PERIODIC, 1, 5, 0x99874fedU },
+  { P4EST_CONFIG_PERIODIC, 2, 5, 0x575af6d5U },
+  { P4EST_CONFIG_PERIODIC, 7, 6, 0xbc35524aU },
+  { P4EST_CONFIG_ROTWRAP, 2, 6, 0x372f7402U },
+  { P4EST_CONFIG_ROTWRAP, 7, 6, 0xa2f1ee48U },
+  { P4EST_CONFIG_TWOCUBES, 5, 6, 0xa8b1f54eU },
+  { P4EST_CONFIG_TWOCUBES, 8, 5, 0x98d3579dU },
+  { P4EST_CONFIG_ROTCUBES, 1, 5, 0x404e4aa8U },
+  { P4EST_CONFIG_ROTCUBES, 7, 6, 0x4c381706U },
+  { P4EST_CONFIG_SHELL, 1, 4, 0x8c56f159U },
+  { P4EST_CONFIG_SHELL, 3, 5, 0xafbc4f8cU },
+  { P4EST_CONFIG_SHELL, 5, 6, 0xf6d9efb8U },
+#endif
+  { P4EST_CONFIG_NULL, 0, 0, 0 }
+};
+
+static const timings_regression_t regression_latest[] =
+{
+#ifndef P4_TO_P8
+  { P4EST_CONFIG_UNIT, 1, 10, 0x6e3e83c4U },
+  { P4EST_CONFIG_UNIT, 1, 11, 0x334bc3deU },
+  { P4EST_CONFIG_UNIT, 64, 14, 0xad908ce4U },
+  { P4EST_CONFIG_UNIT, 256, 15, 0x9e7da646U },
+  { P4EST_CONFIG_STAR, 1, 6, 0x14107b57U },
+  { P4EST_CONFIG_STAR, 4, 6, 0x14107b57U },
+  { P4EST_CONFIG_STAR, 52, 13, 0xc86c74d9U },
+  { P4EST_CONFIG_STAR, 64, 13, 0xc86c74d9U },
+#else
+  { P4EST_CONFIG_UNIT, 1, 5, 0xc2012e84U },
+  { P4EST_CONFIG_UNIT, 1, 6, 0x2cad814dU },
+  { P4EST_CONFIG_UNIT, 3, 8, 0xeb252238U },
+  { P4EST_CONFIG_PERIODIC, 1, 5, 0x2776c9b7U },
+  { P4EST_CONFIG_PERIODIC, 2, 5, 0x2776c9b7U },
+  { P4EST_CONFIG_PERIODIC, 7, 6, 0x4f281079U },
+  { P4EST_CONFIG_ROTWRAP, 2, 6, 0x372f7402U },
+  { P4EST_CONFIG_ROTWRAP, 7, 6, 0x372f7402U },
+  { P4EST_CONFIG_TWOCUBES, 5, 6, 0xa8b1f54eU },
+  { P4EST_CONFIG_TWOCUBES, 8, 5, 0x0aad11d0U },
+  { P4EST_CONFIG_ROTCUBES, 1, 5, 0x404e4aa8U },
+  { P4EST_CONFIG_ROTCUBES, 7, 6, 0x4c381706U },
+  { P4EST_CONFIG_SHELL, 1, 4, 0x8c56f159U },
+  { P4EST_CONFIG_SHELL, 3, 5, 0xafbc4f8cU },
+  { P4EST_CONFIG_SHELL, 5, 6, 0xf6d9efb8U },
+#endif
+  { P4EST_CONFIG_NULL, 0, 0, 0 }
+};
+/* *INDENT-ON* */
+
+static int
+refine_fractal (p4est_t * p4est, p4est_topidx_t which_tree,
+                p4est_quadrant_t * q)
+{
+  int                 qid;
+
+  if ((int) q->level >= refine_level) {
+    return 0;
+  }
+  if ((int) q->level < refine_level - level_shift) {
+    return 1;
+  }
+
+  qid = p4est_quadrant_child_id (q);
+  return (qid == 0 || qid == 3
+#ifdef P4_TO_P8
+          || qid == 5 || qid == 6
+#endif
+    );
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 i;
+  int                 mpiret;
+  int                 wrongusage;
+  unsigned            crc, gcrc;
+  const char         *config_name;
+  const char         *load_name;
+  p4est_locidx_t     *quadrant_counts;
+  p4est_gloidx_t      count_refined, count_balanced;
+  p4est_gloidx_t      prev_quadrant, next_quadrant;
+  p4est_gloidx_t      global_shipped;
+  p4est_connectivity_t *connectivity;
+  p4est_t            *p4est;
+  p4est_nodes_t      *nodes = NULL;
+  p4est_ghost_t      *ghost;
+  p4est_lnodes_t     *lnodes;
+  const timings_regression_t *r, *regression;
+  timings_config_t    config;
+  sc_statinfo_t       stats[TIMINGS_NUM_STATS];
+  sc_flopinfo_t       fi, snapshot;
+  mpi_context_t       mpi_context, *mpi = &mpi_context;
+  sc_options_t       *opt;
+  int                 overlap;
+  int                 subtree;
+  int                 borders;
+  int                 max_ranges;
+  int                 use_ranges, use_ranges_notify, use_balance_verify;
+  int                 oldschool, generate;
+  int                 first_argc;
+  int                 test_multiple_orders;
+  int                 skip_nodes, skip_lnodes;
+
+  /* initialize MPI and p4est internals */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpi->mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpi->mpicomm, &mpi->mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpi->mpicomm, &mpi->mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpi->mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+#ifndef P4EST_ENABLE_DEBUG
+  sc_set_log_defaults (NULL, NULL, SC_LP_STATISTICS);
+#endif
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* process command line arguments */
+  P4EST_GLOBAL_PRODUCTIONF ("Size of %dtant: %lld bytes\n", P4EST_DIM,
+                            (long long) sizeof (p4est_quadrant_t));
+
+  opt = sc_options_new (argv[0]);
+
+  sc_options_add_switch (opt, 'o', "new-balance-overlap", &overlap,
+                         "use the new balance overlap algorithm");
+  sc_options_add_switch (opt, 's', "new-balance-subtree", &subtree,
+                         "use the new balance subtree algorithm");
+  sc_options_add_switch (opt, 'b', "new-balance-borders", &borders,
+                         "use borders in balance");
+  sc_options_add_int (opt, 'm', "max-ranges", &max_ranges, -1,
+                      "override p4est_num_ranges");
+  sc_options_add_switch (opt, 'r', "ranges", &use_ranges,
+                         "use ranges in balance");
+  sc_options_add_switch (opt, 't', "ranges-notify", &use_ranges_notify,
+                         "use both ranges and notify");
+  sc_options_add_switch (opt, 'y', "balance-verify", &use_balance_verify,
+                         "use verifications in balance");
+  sc_options_add_int (opt, 'l', "level", &refine_level, 0,
+                      "initial refine level");
+#ifndef P4_TO_P8
+  sc_options_add_string (opt, 'c', "configuration", &config_name, "unit",
+                         "configuration: unit|periodic|three|moebius|star");
+#else
+  sc_options_add_string (opt, 'c', "configuration", &config_name, "unit",
+                         "configuration: unit|periodic|rotwrap|twocubes|rotcubes|shell");
+#endif
+  sc_options_add_bool (opt, 0, "oldschool", &oldschool, 0,
+                       "Use original p4est_new call");
+  sc_options_add_switch (opt, 0, "generate", &generate,
+                         "Generate regression commands");
+  sc_options_add_string (opt, 'f', "load-forest", &load_name, NULL,
+                         "load saved " P4EST_STRING);
+  sc_options_add_switch (opt, 0, "multiple-lnodes",
+                         &test_multiple_orders,
+                         "Also time lnodes for orders 2, 4, and 8");
+  sc_options_add_switch (opt, 0, "skip-nodes", &skip_nodes, "Skip nodes");
+  sc_options_add_switch (opt, 0, "skip-lnodes", &skip_lnodes, "Skip lnodes");
+
+  first_argc = sc_options_parse (p4est_package_id, SC_LP_DEFAULT,
+                                 opt, argc, argv);
+  if (first_argc < 0 || first_argc != argc) {
+    sc_options_print_usage (p4est_package_id, SC_LP_ERROR, opt, NULL);
+    return 1;
+  }
+  if (max_ranges < -1 || max_ranges == 0) {
+    P4EST_GLOBAL_LERROR ("The -m / --max-ranges option must be positive\n");
+    return 1;
+  }
+  sc_options_print_summary (p4est_package_id, SC_LP_PRODUCTION, opt);
+
+  if (skip_lnodes) {
+    if (test_multiple_orders) {
+      SC_GLOBAL_PRODUCTION
+        ("Warning: cannot test multiple lnode orders if --skip-lnodes is given.\n");
+      test_multiple_orders = 0;
+    }
+  }
+  wrongusage = 0;
+  config = P4EST_CONFIG_NULL;
+  if (!strcmp (config_name, "unit")) {
+    config = P4EST_CONFIG_UNIT;
+  }
+  else if (!strcmp (config_name, "periodic")) {
+    config = P4EST_CONFIG_PERIODIC;
+  }
+#ifndef P4_TO_P8
+  else if (!strcmp (config_name, "three")) {
+    config = P4EST_CONFIG_THREE;
+  }
+  else if (!strcmp (config_name, "moebius")) {
+    config = P4EST_CONFIG_MOEBIUS;
+  }
+  else if (!strcmp (config_name, "star")) {
+    config = P4EST_CONFIG_STAR;
+  }
+#else
+  else if (!strcmp (config_name, "rotwrap")) {
+    config = P4EST_CONFIG_ROTWRAP;
+  }
+  else if (!strcmp (config_name, "twocubes")) {
+    config = P4EST_CONFIG_TWOCUBES;
+  }
+  else if (!strcmp (config_name, "rotcubes")) {
+    config = P4EST_CONFIG_ROTCUBES;
+  }
+  else if (!strcmp (config_name, "shell")) {
+    config = P4EST_CONFIG_SHELL;
+  }
+#endif
+  else if (load_name != NULL) {
+    config_name = load_name;
+  }
+  else {
+    wrongusage = 1;
+  }
+  if (wrongusage) {
+    P4EST_GLOBAL_LERRORF ("Wrong configuration name given: %s\n",
+                          config_name);
+    sc_options_print_usage (p4est_package_id, SC_LP_ERROR, opt, NULL);
+    sc_abort_collective ("Usage error");
+  }
+
+  /* get command line argument: maximum refinement level */
+  level_shift = 4;
+
+  /* print general setup information */
+  P4EST_GLOBAL_STATISTICSF
+    ("Processors %d configuration %s level %d shift %d\n", mpi->mpisize,
+     config_name, refine_level, level_shift);
+
+  /* start overall timing */
+  mpiret = sc_MPI_Barrier (mpi->mpicomm);
+  SC_CHECK_MPI (mpiret);
+  sc_flops_start (&fi);
+
+  /* create connectivity and forest structures */
+  regression = NULL;
+  if (load_name == NULL) {
+#ifndef P4_TO_P8
+    if (config == P4EST_CONFIG_PERIODIC) {
+      connectivity = p4est_connectivity_new_periodic ();
+    }
+    else if (config == P4EST_CONFIG_THREE) {
+      connectivity = p4est_connectivity_new_corner ();
+    }
+    else if (config == P4EST_CONFIG_MOEBIUS) {
+      connectivity = p4est_connectivity_new_moebius ();
+    }
+    else if (config == P4EST_CONFIG_STAR) {
+      connectivity = p4est_connectivity_new_star ();
+    }
+    else {
+      connectivity = p4est_connectivity_new_unitsquare ();
+    }
+#else
+    if (config == P4EST_CONFIG_PERIODIC) {
+      connectivity = p8est_connectivity_new_periodic ();
+    }
+    else if (config == P4EST_CONFIG_ROTWRAP) {
+      connectivity = p8est_connectivity_new_rotwrap ();
+    }
+    else if (config == P4EST_CONFIG_TWOCUBES) {
+      connectivity = p8est_connectivity_new_twocubes ();
+    }
+    else if (config == P4EST_CONFIG_ROTCUBES) {
+      connectivity = p8est_connectivity_new_rotcubes ();
+    }
+    else if (config == P4EST_CONFIG_SHELL) {
+      connectivity = p8est_connectivity_new_shell ();
+    }
+    else {
+      connectivity = p8est_connectivity_new_unitcube ();
+    }
+#endif
+
+    /* create new p4est from scratch */
+    if (oldschool) {
+      regression = regression_oldschool;
+      p4est = p4est_new_ext (mpi->mpicomm, connectivity,
+                             15, 0, 0, 0, NULL, NULL);
+    }
+    else {
+      regression = regression_latest;
+      p4est = p4est_new_ext (mpi->mpicomm, connectivity,
+                             0, refine_level - level_shift, 1, 0, NULL, NULL);
+    }
+
+    /* print all available regression tests */
+    if (generate) {
+      const char         *config_name = NULL;
+
+      P4EST_GLOBAL_PRODUCTION ("Checksum regression tests available:\n");
+      for (r = regression; r->config != P4EST_CONFIG_NULL; ++r) {
+        switch (r->config) {
+        case P4EST_CONFIG_PERIODIC:
+          config_name = "periodic";
+          break;
+#ifndef P4_TO_P8
+        case P4EST_CONFIG_THREE:
+          config_name = "three";
+          break;
+        case P4EST_CONFIG_MOEBIUS:
+          config_name = "moebius";
+          break;
+        case P4EST_CONFIG_STAR:
+          config_name = "star";
+          break;
+#else
+        case P4EST_CONFIG_ROTWRAP:
+          config_name = "rotwrap";
+          break;
+        case P4EST_CONFIG_TWOCUBES:
+          config_name = "twocubes";
+          break;
+        case P4EST_CONFIG_ROTCUBES:
+          config_name = "rotcubes";
+          break;
+        case P4EST_CONFIG_SHELL:
+          config_name = "shell";
+          break;
+#endif
+        default:
+          config_name = "unit";
+          break;
+        }
+        P4EST_GLOBAL_PRODUCTIONF ("mpirun -np %3d %s%s -c %10s -l %2d\n",
+                                  r->mpisize, opt->program_path,
+                                  oldschool ? " --oldschool" : "",
+                                  config_name, r->level);
+      }
+    }
+  }
+  else {
+    p4est = p4est_load (load_name, mpi->mpicomm, 0, 0, NULL, &connectivity);
+  }
+
+  p4est->inspect = P4EST_ALLOC_ZERO (p4est_inspect_t, 1);
+  p4est->inspect->use_balance_ranges = use_ranges;
+  p4est->inspect->use_balance_ranges_notify = use_ranges_notify;
+  p4est->inspect->use_balance_verify = use_balance_verify;
+  p4est->inspect->balance_max_ranges = max_ranges;
+  P4EST_GLOBAL_STATISTICSF
+    ("Balance: new overlap %d new subtree %d borders %d\n", overlap,
+     (overlap && subtree), (overlap && borders));
+  quadrant_counts = P4EST_ALLOC (p4est_locidx_t, p4est->mpisize);
+
+  /* time refine */
+  sc_flops_snap (&fi, &snapshot);
+  p4est_refine (p4est, 1, refine_fractal, NULL);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_REFINE], snapshot.iwtime, "Refine");
+#ifdef P4EST_TIMINGS_VTK
+  p4est_vtk_write_file (p4est, "timings_refined");
+#endif
+  count_refined = p4est->global_num_quadrants;
+
+  /* time balance */
+  sc_flops_snap (&fi, &snapshot);
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_BALANCE], snapshot.iwtime, "Balance");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_A],
+                 p4est->inspect->balance_A, "Balance A time");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_COMM],
+                 p4est->inspect->balance_comm, "Balance comm time");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_B],
+                 p4est->inspect->balance_B, "Balance B time");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_A_COUNT_IN],
+                 (double) p4est->inspect->balance_A_count_in,
+                 "Balance A count inlist");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_A_COUNT_OUT],
+                 (double) p4est->inspect->balance_A_count_out,
+                 "Balance A count outlist");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_COMM_SENT],
+                 (double) p4est->inspect->balance_comm_sent,
+                 "Balance sent second round");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_COMM_NZPEERS],
+                 (double) p4est->inspect->balance_comm_nzpeers,
+                 "Balance nonzero peers second round");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_B_COUNT_IN],
+                 (double) p4est->inspect->balance_B_count_in,
+                 "Balance B count inlist");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_B_COUNT_OUT],
+                 (double) p4est->inspect->balance_B_count_out,
+                 "Balance B count outlist");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_RANGES],
+                 p4est->inspect->balance_ranges, "Balance time for ranges");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_NOTIFY],
+                 p4est->inspect->balance_notify, "Balance time for notify");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_NOTIFY_ALLGATHER],
+                 p4est->inspect->balance_notify_allgather,
+                 "Balance time for notify_allgather");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_A_ZERO_RECEIVES],
+                 p4est->inspect->balance_zero_receives[0],
+                 "Balance A zero receives");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_A_ZERO_SENDS],
+                 p4est->inspect->balance_zero_sends[0],
+                 "Balance A zero sends");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_B_ZERO_RECEIVES],
+                 p4est->inspect->balance_zero_receives[1],
+                 "Balance B zero receives");
+  sc_stats_set1 (&stats[TIMINGS_BALANCE_B_ZERO_SENDS],
+                 p4est->inspect->balance_zero_sends[1],
+                 "Balance B zero sends");
+#ifdef P4EST_TIMINGS_VTK
+  p4est_vtk_write_file (p4est, "timings_balanced");
+#endif
+  count_balanced = p4est->global_num_quadrants;
+  crc = p4est_checksum (p4est);
+
+  /* time rebalance - is a noop on the tree */
+  sc_flops_snap (&fi, &snapshot);
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_REBALANCE], snapshot.iwtime, "Rebalance");
+  sc_stats_set1 (&stats[TIMINGS_REBALANCE_A],
+                 p4est->inspect->balance_A, "Rebalance A time");
+  sc_stats_set1 (&stats[TIMINGS_REBALANCE_COMM],
+                 p4est->inspect->balance_comm, "Rebalance comm time");
+  sc_stats_set1 (&stats[TIMINGS_REBALANCE_B],
+                 p4est->inspect->balance_B, "Rebalance B time");
+  sc_stats_set1 (&stats[TIMINGS_REBALANCE_A_COUNT_IN],
+                 (double) p4est->inspect->balance_A_count_in,
+                 "Rebalance A count inlist");
+  sc_stats_set1 (&stats[TIMINGS_REBALANCE_A_COUNT_OUT],
+                 (double) p4est->inspect->balance_A_count_out,
+                 "Rebalance A count outlist");
+  sc_stats_set1 (&stats[TIMINGS_REBALANCE_COMM_SENT],
+                 (double) p4est->inspect->balance_comm_sent,
+                 "Rebalance sent second round");
+  sc_stats_set1 (&stats[TIMINGS_REBALANCE_COMM_NZPEERS],
+                 (double) p4est->inspect->balance_comm_nzpeers,
+                 "Rebalance nonzero peers second round");
+  sc_stats_set1 (&stats[TIMINGS_REBALANCE_B_COUNT_IN],
+                 (double) p4est->inspect->balance_B_count_in,
+                 "Rebalance B count inlist");
+  sc_stats_set1 (&stats[TIMINGS_REBALANCE_B_COUNT_OUT],
+                 (double) p4est->inspect->balance_B_count_out,
+                 "Rebalance B count outlist");
+  P4EST_ASSERT (count_balanced == p4est->global_num_quadrants);
+  P4EST_ASSERT (crc == p4est_checksum (p4est));
+
+  /* time a uniform partition */
+  sc_flops_snap (&fi, &snapshot);
+  p4est_partition (p4est, 0, NULL);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_PARTITION], snapshot.iwtime, "Partition");
+#ifdef P4EST_TIMINGS_VTK
+  p4est_vtk_write_file (p4est, "timings_partitioned");
+#endif
+  P4EST_ASSERT (crc == p4est_checksum (p4est));
+
+  /* time building the ghost layer */
+  sc_flops_snap (&fi, &snapshot);
+  ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_GHOSTS], snapshot.iwtime, "Ghost layer");
+  gcrc = p4est_ghost_checksum (p4est, ghost);
+
+  /* time the node numbering */
+  if (!skip_nodes) {
+    sc_flops_snap (&fi, &snapshot);
+    nodes = p4est_nodes_new (p4est, ghost);
+    sc_flops_shot (&fi, &snapshot);
+    sc_stats_set1 (&stats[TIMINGS_NODES], snapshot.iwtime, "Nodes");
+  }
+  else {
+    sc_stats_set1 (&stats[TIMINGS_NODES], 0., "Nodes");
+  }
+
+  /* set this anyway so the output format is dimension independent */
+  sc_stats_set1 (&stats[TIMINGS_TRILINEAR_OBSOLETE], 0., "Unused");
+
+  if (!skip_nodes) {
+    p4est_nodes_destroy (nodes);
+  }
+
+  /* time the lnode numbering */
+  if (!skip_lnodes) {
+    sc_flops_snap (&fi, &snapshot);
+    lnodes = p4est_lnodes_new (p4est, ghost, 1);
+    sc_flops_shot (&fi, &snapshot);
+    sc_stats_set1 (&stats[TIMINGS_LNODES], snapshot.iwtime, "L-Nodes");
+    p4est_lnodes_destroy (lnodes);
+  }
+  else {
+    sc_stats_set1 (&stats[TIMINGS_LNODES], 0., "L-Nodes");
+  }
+
+  if (test_multiple_orders) {
+    sc_flops_snap (&fi, &snapshot);
+    lnodes = p4est_lnodes_new (p4est, ghost, 3);
+    sc_flops_shot (&fi, &snapshot);
+    sc_stats_set1 (&stats[TIMINGS_LNODES3], snapshot.iwtime, "L-Nodes 3");
+    p4est_lnodes_destroy (lnodes);
+
+    sc_flops_snap (&fi, &snapshot);
+    lnodes = p4est_lnodes_new (p4est, ghost, 7);
+    sc_flops_shot (&fi, &snapshot);
+    sc_stats_set1 (&stats[TIMINGS_LNODES7], snapshot.iwtime, "L-Nodes 7");
+    p4est_lnodes_destroy (lnodes);
+  }
+  else {
+    sc_stats_set1 (&stats[TIMINGS_LNODES3], 0., "L-Nodes 3");
+    sc_stats_set1 (&stats[TIMINGS_LNODES7], 0., "L-Nodes 7");
+  }
+
+  p4est_ghost_destroy (ghost);
+
+  /* time a partition with a shift of all elements by one processor */
+  for (i = 0, next_quadrant = 0; i < p4est->mpisize; ++i) {
+    prev_quadrant = next_quadrant;
+    next_quadrant = (p4est->global_num_quadrants * (i + 1)) / p4est->mpisize;
+    quadrant_counts[i] = (p4est_locidx_t) (next_quadrant - prev_quadrant);
+  }
+  if (p4est->mpisize > 1) {
+    quadrant_counts[0] += quadrant_counts[p4est->mpisize - 1];  /* same type */
+    quadrant_counts[p4est->mpisize - 1] = 0;
+  }
+
+  sc_flops_snap (&fi, &snapshot);
+  global_shipped = p4est_partition_given (p4est, quadrant_counts);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TIMINGS_REPARTITION], snapshot.iwtime, "Repartition");
+
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Done " P4EST_STRING "_partition_given shipped %lld quadrants %.3g%%\n",
+     (long long) global_shipped,
+     global_shipped * 100. / p4est->global_num_quadrants);
+  P4EST_ASSERT (crc == p4est_checksum (p4est));
+
+  /* verify forest checksum */
+  if (regression != NULL && mpi->mpirank == 0) {
+    for (r = regression; r->config != P4EST_CONFIG_NULL; ++r) {
+      if (r->config != config || r->mpisize != mpi->mpisize
+          || r->level != refine_level)
+        continue;
+      SC_CHECK_ABORT (crc == r->checksum, "Checksum mismatch");
+      P4EST_GLOBAL_INFO ("Checksum regression OK\n");
+      break;
+    }
+  }
+
+  /* print status and checksum */
+  P4EST_GLOBAL_STATISTICSF ("Processors %d level %d shift %d"
+                            " checksums 0x%08x 0x%08x\n",
+                            mpi->mpisize, refine_level, level_shift,
+                            crc, gcrc);
+  P4EST_GLOBAL_STATISTICSF ("Level %d refined to %lld balanced to %lld\n",
+                            refine_level, (long long) count_refined,
+                            (long long) count_balanced);
+
+  /* calculate and print timings */
+  sc_stats_compute (mpi->mpicomm, TIMINGS_NUM_STATS, stats);
+  sc_stats_print (p4est_package_id, SC_LP_STATISTICS,
+                  TIMINGS_NUM_STATS, stats, 1, 1);
+
+  /* destroy the p4est and its connectivity structure */
+  P4EST_FREE (quadrant_counts);
+  P4EST_FREE (p4est->inspect);
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (connectivity);
+
+  sc_options_destroy (opt);
+
+  /* clean up and exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/timings/timings3.c b/example/timings/timings3.c
new file mode 100644
index 0000000..35154c9
--- /dev/null
+++ b/example/timings/timings3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "timings2.c"
diff --git a/example/timings/tsearch3.c b/example/timings/tsearch3.c
new file mode 100644
index 0000000..5db5a6b
--- /dev/null
+++ b/example/timings/tsearch3.c
@@ -0,0 +1,1062 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * Usage:
+ * p8est_tsearch [-l <LEVEL>] [-s <LEVEL-SHIFT>] [-N <NUM-POINTS>] [-V]
+ *
+ * NUM-POINTS determines how many points are positioned randomly and searched
+ * in the forest.  The domain is the spherical shell with radii 0.55 and 1 and
+ * the points are uniformly distributed in the unit cube.
+ */
+
+#include <p4est_to_p8est.h>
+#ifndef P4_TO_P8
+#error "This program is currently intended for 3D only"
+#else
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_search.h>
+#include <p8est_vtk.h>
+#endif
+#include <sc_flops.h>
+#include <sc_options.h>
+#include <sc_statistics.h>
+
+static int          refine_level, level_shift;
+
+typedef enum tsearch_stats
+{
+  TSEARCH_NEW,
+  TSEARCH_REFINE,
+  TSEARCH_BALANCE,
+  TSEARCH_PARTITION,
+  TSEARCH_SEARCH_1,
+  TSEARCH_SEARCH_N,
+  TSEARCH_NUM_STATS
+}
+tsearch_stats_t;
+
+typedef struct
+{
+  /* MPI related data */
+  sc_MPI_Comm         mpicomm;
+  int                 mpisize;
+  int                 mpirank;
+
+  /* global data for the program */
+  size_t              matches;
+  double              rout, rin;
+  double              rout2, rin2;
+  double              routbyrin, logrbyr;
+  double              expected;
+  sc_array_t         *points;
+  int                 test_rays;
+  int                 skip_1;
+
+  /* data for the currently active quadrant */
+  p4est_locidx_t      which_tree;
+  p4est_quadrant_t   *sq;
+  int                 is_leaf;
+  double              width, radius2, R2, r2;
+  double              qref[P4EST_DIM], center[P4EST_DIM];
+  double              qref2[P4EST_DIM];
+  double              bbox[3][2];
+  double              planes[2][3][3];  /* (x,y) * (min,mid,max) * dir(3) */
+  double             *ray_lengths;
+  double             *ray_lengths_expected;
+}
+tsearch_global_t;
+
+static int
+refine_local_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+                 p4est_quadrant_t * q)
+{
+  if ((int) q->level >= refine_level) {
+    return 0;
+  }
+  if ((int) q->level < refine_level - level_shift) {
+    return 1;
+  }
+
+  return which_tree == 0 && p4est_quadrant_child_id (q) == 0;
+}
+
+static int
+refine_fractal_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+                   p4est_quadrant_t * q)
+{
+  int                 qid;
+
+  if ((int) q->level >= refine_level) {
+    return 0;
+  }
+  if ((int) q->level < refine_level - level_shift) {
+    return 1;
+  }
+
+  qid = p4est_quadrant_child_id (q);
+  return (qid == 0 || qid == 3
+#ifdef P4_TO_P8
+          || qid == 5 || qid == 6
+#endif
+    );
+}
+
+typedef struct tsearch_point
+{
+  double              xy[P4EST_DIM];
+  p4est_locidx_t      lid;
+}
+tsearch_point_t;
+
+/* *INDENT-OFF* */
+/* lookup table for the permutations of the coordinate axes in the shell */
+static const int    rtop[6][3] = {{ 2, 0, 1 },
+                                  { 1, 0, 2 },
+                                  { 2, 0, 1 },
+                                  { 1, 0, 2 },
+                                  { 0, 2, 1 },
+                                  { 0, 2, 1 }};
+static const double rsign[6][3] = {{  1., -1., -1. },
+                                   { -1., -1., -1. },
+                                   { -1., -1.,  1. },
+                                   {  1., -1.,  1. },
+                                   { -1.,  1.,  1. },
+                                   {  1., -1.,  1. }};
+/* *INDENT-ON* */
+
+static void
+map_planes (const double *ref, p4est_topidx_t which_tree,
+            double planeeast[3], double planenorth[3])
+{
+  double              qpole[3];
+  double              pole[3];
+  double              qeast[3];
+  double              east[3];
+  double              qnorth[3];
+  double              north[3];
+  int                 j;
+  const int          *pi;
+  const double       *si;
+  double              sinx, cosx;
+  double              siny, cosy;
+
+  /* assert that input coordinates are in the expected range */
+  P4EST_ASSERT (ref[0] < 1.0 + 1e-12 && ref[0] > -1.0 - 1e-12);
+  P4EST_ASSERT (ref[1] < 1.0 + 1e-12 && ref[1] > -1.0 - 1e-12);
+  P4EST_ASSERT (ref[2] < 1.0 + 1e-12 && ref[2] > -1e-12);
+
+  qpole[0] = 0.;
+  qpole[1] = 0.;
+  qpole[2] = 1.;
+  qeast[0] = 1.;
+  qeast[1] = 0.;
+  qeast[2] = 0.;
+  qnorth[0] = 0.;
+  qnorth[1] = 1.;
+  qnorth[2] = 0.;
+
+  pi = rtop[which_tree / 4];
+  si = rsign[which_tree / 4];
+  for (j = 0; j < P4EST_DIM; ++j) {
+    pole[j] = si[j] * qpole[pi[j]];
+    east[j] = si[j] * qeast[pi[j]];
+    north[j] = si[j] * qnorth[pi[j]];
+  }
+
+  sinx = sin (ref[0] * M_PI_4);
+  cosx = cos (ref[0] * M_PI_4);
+  siny = sin (ref[1] * M_PI_4);
+  cosy = cos (ref[1] * M_PI_4);
+
+  for (j = 0; j < P4EST_DIM; j++) {
+    planeeast[j] = cosx * east[j] - sinx * pole[j];
+    planenorth[j] = cosy * north[j] - siny * pole[j];
+  }
+}
+
+static void
+reference_to_physical (tsearch_global_t * tsg, const double *ref,
+                       double *phys)
+{
+  const int          *pi;
+  const double       *si;
+  int                 j;
+  double              qvalues[6];
+  double              x, y;
+  double              R, q;
+
+  /* assert that input coordinates are in the expected range */
+  P4EST_ASSERT (ref[0] < 1.0 + 1e-12 && ref[0] > -1.0 - 1e-12);
+  P4EST_ASSERT (ref[1] < 1.0 + 1e-12 && ref[1] > -1.0 - 1e-12);
+  P4EST_ASSERT (ref[2] < 1.0 + 1e-12 && ref[2] > -1e-12);
+
+  /* transform x and y for nicer grading */
+  x = tan (ref[0] * M_PI_4);
+  y = tan (ref[1] * M_PI_4);
+
+  /* compute transformation ingredients */
+  R = tsg->rin * pow (tsg->routbyrin, ref[2]);
+  q = R / sqrt (x * x + y * y + 1.);
+
+  /* compute physical coordinate values */
+  qvalues[0] = q * x;
+  qvalues[1] = q * y;
+  qvalues[2] = q;
+
+  /* assign coordinates based on tree id */
+#if 1
+  pi = rtop[tsg->which_tree / 4];
+  si = rsign[tsg->which_tree / 4];
+  for (j = 0; j < P4EST_DIM; ++j) {
+    phys[j] = si[j] * qvalues[pi[j]];
+  }
+#else
+  switch (tsg->which_tree / 4) {
+  case 3:                      /* top */
+    phys[0] = +q * y;
+    phys[1] = -q * x;
+    phys[2] = +q;
+    break;
+  case 2:                      /* left */
+    phys[0] = -q;
+    phys[1] = -q * x;
+    phys[2] = +q * y;
+    break;
+  case 1:                      /* bottom */
+    phys[0] = -q * y;
+    phys[1] = -q * x;
+    phys[2] = -q;
+    break;
+  case 0:                      /* right */
+    phys[0] = +q;
+    phys[1] = -q * x;
+    phys[2] = -q * y;
+    break;
+  case 4:                      /* back */
+    phys[0] = -q * x;
+    phys[1] = +q;
+    phys[2] = +q * y;
+    break;
+  case 5:                      /* front */
+    phys[0] = +q * x;
+    phys[1] = -q;
+    phys[2] = +q * y;
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+#endif
+}
+
+/** Return true if and only if the reference coordinates match the tree. */
+static int
+physical_to_reference (tsearch_global_t * tsg, const double *phys,
+                       double *ref)
+{
+  const int          *pi;
+  const double       *si;
+  int                 j;
+  double              q, R2;
+  double              qvalues[3];
+  const p4est_topidx_t tid = tsg->which_tree;
+
+  /* reverse permutation of coordinate axes and compute radius */
+  R2 = 0.;
+  pi = rtop[tid / 4];
+  si = rsign[tid / 4];
+  for (j = 0; j < P4EST_DIM; ++j) {
+    q = qvalues[pi[j]] = si[j] * phys[j];
+    R2 += q * q;
+  }
+
+  /* recover the reference coordinates */
+  for (j = 0; j < 2; ++j) {
+    q = ref[j] = atan2 (qvalues[j], qvalues[2]) / M_PI_4;
+    /* check x and y coordinate range */
+    if (!(tid & (1 << j))) {
+      q += 1.;
+    }
+    if (q < 0. || q > 1.) {
+      return 0;
+    }
+  }
+  q = ref[2] = log (sqrt (R2) / tsg->rin) / tsg->logrbyr;
+
+  return q >= 0. && q <= 1.;
+}
+
+/** Rays **/
+typedef struct ts_ray
+{
+  double              dir[3];   /* normal vector */
+  double              off[3];   /* point on the ray closest to the origin */
+  /* a ray is parametrized as off + s * dir */
+  double              idir[3];  /* 1. / dir */
+  double              off2;     /* off' * off */
+  /* by choosing off to be the closest point to the origin,
+   * we guarantee off' * dir = 0 */
+  /* by chooseing dir to be normal, dir' * dir = 1 */
+  p4est_locidx_t      lidx;
+}
+ts_ray_t;
+
+static int
+ts_ray_sphere_test (ts_ray_t * ray, double R2, double minmax[2])
+{
+  double              o2 = ray->off2;
+  double              val;
+
+  if (o2 > R2) {
+    return 0;
+  }
+  else {
+    val = sqrt (R2 - o2);
+    minmax[0] = -val;
+    minmax[1] = val;
+
+    return 1;
+  }
+}
+
+static int
+ts_ray_bounding_box_test (ts_ray_t * ray, double bounds[3][2],
+                          double minmax[2])
+{
+  double              o[3] = { ray->off[0], ray->off[1], ray->off[2] };
+  double              id[3] = { ray->idir[0], ray->idir[1], ray->idir[2] };
+  double              b[3][2] = { {bounds[0][0], bounds[0][1]},
+  {bounds[1][0], bounds[1][1]},
+  {bounds[2][0], bounds[2][1]}
+  };
+  double              s[3][2];
+  double              min, max;
+  double              dmin, dmax;
+  int                 i, j;
+
+  min = minmax[0];
+  max = minmax[1];
+
+  for (i = 0; i < 3; i++) {
+    /* intercepts of parameter s */
+    for (j = 0; j < 2; j++) {
+      s[i][j] = (b[i][j] - o[i]) * id[i];
+    }
+    /* overlap interval */
+    if (s[i][0] < s[i][1]) {
+      dmin = s[i][0];
+      dmax = s[i][1];
+    }
+    else {
+      dmin = s[i][1];
+      dmax = s[i][0];
+    }
+    /* if overlap interval does not intersect
+     * previous overlap interval */
+    if (min > dmax || dmin > max) {
+      return 0;
+    }
+    min = SC_MAX (min, dmin);
+    max = SC_MIN (max, dmax);
+  }
+
+  minmax[0] = min;
+  minmax[1] = max;
+
+  return 1;
+}
+
+static void
+tsearch_setup (tsearch_global_t * tsg)
+{
+  int                 j, k;
+  double              mlen, hwidth, dist2;
+  double              ref[P4EST_DIM], phys[P4EST_DIM];
+  double             *qref = tsg->qref;
+  double             *qref2 = tsg->qref2;
+  double              cref[3];
+  double             *center = tsg->center;
+  p4est_quadrant_t   *q = tsg->sq, c;
+#ifdef P4EST_ENABLE_DEBUG
+  int                 retval;
+  double              nref[P4EST_DIM];
+#endif
+  double              bbox[3][2] = { {1. / 0., -1. / 0.},
+  {1. / 0., -1. / 0.},
+  {1. / 0., -1. / 0.}
+  };
+
+  mlen = 1. / P4EST_ROOT_LEN;
+  hwidth = .5 * (tsg->width = mlen * P4EST_QUADRANT_LEN (q->level));
+
+  /* transform center of the quadrant to physical space */
+  ref[0] = cref[0] = q->x * mlen + hwidth - 1. + (tsg->which_tree & 1);
+  ref[1] = cref[1] = q->y * mlen + hwidth - 1. + (tsg->which_tree & 2) / 2;
+  ref[2] = cref[2] = q->z * mlen + hwidth;
+  reference_to_physical (tsg, ref, center);
+
+#ifdef P4EST_ENABLE_DEBUG
+  retval = physical_to_reference (tsg, center, nref);
+  P4EST_ASSERT (retval);
+  for (j = 0; j < P4EST_DIM; ++j) {
+    P4EST_ASSERT (fabs (ref[j] - nref[j]) < 1e-10);
+  }
+#endif
+
+  /* transform all corners of the quadrant and take max distance to center */
+  if (!tsg->test_rays) {
+    tsg->radius2 = 0.;
+    for (k = 0; k < P4EST_CHILDREN; ++k) {
+      p4est_quadrant_corner_node (q, k, &c);
+      ref[0] = c.x * mlen - 1. + (tsg->which_tree & 1);
+      ref[1] = c.y * mlen - 1. + (tsg->which_tree & 2) / 2;
+      ref[2] = c.z * mlen;
+      reference_to_physical (tsg, ref, phys);
+      dist2 = 0.;
+      for (j = 0; j < P4EST_DIM; ++j) {
+        dist2 += (phys[j] - center[j]) * (phys[j] - center[j]);
+      }
+      if (dist2 > tsg->radius2) {
+        tsg->radius2 = dist2;
+      }
+      if (k == 0) {
+        qref[0] = ref[0];
+        qref[1] = ref[1];
+        qref[2] = ref[2];
+      }
+    }
+  }
+  else {
+    tsg->R2 = 0.;
+    tsg->r2 = 1. / 0.;
+    for (k = 0; k < P4EST_CHILDREN; ++k) {
+      p4est_quadrant_corner_node (q, k, &c);
+      ref[0] = c.x * mlen - 1. + (tsg->which_tree & 1);
+      ref[1] = c.y * mlen - 1. + (tsg->which_tree & 2) / 2;
+      ref[2] = c.z * mlen;
+      reference_to_physical (tsg, ref, phys);
+      for (j = 0; j < P4EST_DIM; ++j) {
+        bbox[j][0] = SC_MIN (bbox[j][0], phys[j]);
+        bbox[j][1] = SC_MAX (bbox[j][1], phys[j]);
+      }
+      tsg->R2 =
+        SC_MAX (tsg->R2,
+                phys[0] * phys[0] + phys[1] * phys[1] + phys[2] * phys[2]);
+      tsg->r2 =
+        SC_MIN (tsg->r2,
+                phys[0] * phys[0] + phys[1] * phys[1] + phys[2] * phys[2]);
+      if (!k) {
+        qref[0] = ref[0];
+        qref[1] = ref[1];
+        qref[2] = ref[2];
+      }
+      else if (k == P4EST_CHILDREN - 1) {
+        qref2[0] = ref[0];
+        qref2[1] = ref[1];
+        qref2[2] = ref[2];
+      }
+    }
+    for (j = 0; j < 3; j++) {
+      tsg->bbox[j][0] = bbox[j][0];
+      tsg->bbox[j][1] = bbox[j][1];
+    }
+    if (tsg->is_leaf) {
+      map_planes (qref, tsg->which_tree, &tsg->planes[0][0][0],
+                  &tsg->planes[1][0][0]);
+      map_planes (cref, tsg->which_tree, &tsg->planes[0][1][0],
+                  &tsg->planes[1][1][0]);
+      map_planes (qref2, tsg->which_tree, &tsg->planes[0][2][0],
+                  &tsg->planes[1][2][0]);
+    }
+  }
+}
+
+static int
+time_search_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+                p4est_quadrant_t * q, p4est_locidx_t local_num, void *point)
+{
+  tsearch_global_t   *tsg = (tsearch_global_t *) p4est->user_pointer;
+  int                 i, j;
+  double              width, r2;
+  double              ref[P4EST_DIM];
+  const double       *qref;
+
+  if (point == NULL) {
+    /* per-quadrant setup function */
+    tsg->which_tree = which_tree;
+    tsg->sq = q;
+    tsg->is_leaf = local_num >= 0;
+    tsearch_setup (tsg);
+    return 1;
+  }
+  P4EST_ASSERT (tsg->sq == q);
+  P4EST_ASSERT (tsg->is_leaf == (local_num >= 0));
+
+  if (!tsg->test_rays) {
+    tsearch_point_t    *t = (tsearch_point_t *) point;
+
+    /* root level check to see if the point is contained in the shell */
+    if (q->level == 0) {
+      r2 = t->xy[0] * t->xy[0] + t->xy[1] * t->xy[1] + t->xy[2] * t->xy[2];
+      if (r2 >= tsg->rout2 || r2 <= tsg->rin2) {
+        return 0;
+      }
+    }
+
+    if (!tsg->is_leaf) {
+      /* perform over-optimistic check with the quadrant's bounding sphere */
+      r2 = 0.;
+      for (j = 0; j < P4EST_DIM; ++j) {
+        r2 += (t->xy[j] - tsg->center[j]) * (t->xy[j] - tsg->center[j]);
+      }
+      return r2 < (1. + 1e-12) * tsg->radius2;
+    }
+    else {
+      /* perform strict check by inverse coordinate transformation */
+      if (physical_to_reference (tsg, t->xy, ref)) {
+        /* the point is contained in the correct tree, now check quadrant */
+        width = tsg->width;
+        qref = tsg->qref;
+        for (j = 0; j < P4EST_DIM; ++j) {
+          if (ref[j] < qref[j] || ref[j] > qref[j] + width) {
+            return 0;
+          }
+        }
+
+        /* we have found a matching quadrant for this point */
+        ++tsg->matches;
+        t->lid = local_num;
+        return 1;
+      }
+
+      /* the return value is irrelevant for leaves */
+      return 0;
+    }
+  }
+  else {
+    ts_ray_t           *ray = (ts_ray_t *) point;
+    double              minmax[2];
+    double              innerminmax[2];
+    double              o[3] = { ray->off[0], ray->off[1], ray->off[2] };
+    double              d[3] = { ray->dir[0], ray->dir[1], ray->dir[2] };
+    double              length;
+
+    /* quick tests */
+    if (!ts_ray_sphere_test (ray, tsg->R2, minmax) ||
+        !ts_ray_bounding_box_test (ray, tsg->bbox, minmax)) {
+      return 0;
+    }
+
+    if (!tsg->is_leaf) {
+      return 1;
+    }
+
+    /* intersect with the planes that define the vertical boundaries of the
+     * physical element */
+    for (i = 0; i < 2; i++) {
+      double              s[3];
+      double              dmin, dmax;
+
+      for (j = 0; j < 3; j++) {
+        double             *plane = &tsg->planes[i][j][0];
+        double              offdotplane;
+        double              dirdotplane;
+
+        offdotplane = plane[0] * o[0] + plane[1] * o[1] + plane[2] * o[2];
+        dirdotplane = plane[0] * d[0] + plane[1] * d[1] + plane[2] * d[2];
+
+        s[j] = -offdotplane / dirdotplane;
+      }
+      if (s[0] < s[2]) {
+        dmin = s[0];
+        dmax = s[2];
+      }
+      else {
+        dmin = s[2];
+        dmax = s[0];
+      }
+      if (s[1] < dmin || s[1] > dmax) {
+        if (dmin <= minmax[0] && minmax[1] <= dmax) {
+          return 0;
+        }
+
+        if (dmax < minmax[1]) {
+          minmax[0] = SC_MAX (minmax[0], dmax);
+        }
+        if (dmin > minmax[0]) {
+          minmax[1] = SC_MIN (minmax[1], dmin);
+        }
+      }
+      else {
+        if (minmax[0] > dmax || dmin > minmax[1]) {
+          return 0;
+        }
+        minmax[0] = SC_MAX (dmin, minmax[0]);
+        minmax[1] = SC_MIN (dmax, minmax[1]);
+      }
+    }
+
+    length = minmax[1] - minmax[0];
+
+    if (length && ts_ray_sphere_test (ray, tsg->r2, innerminmax)) {
+      innerminmax[0] = SC_MAX (innerminmax[0], minmax[0]);
+      innerminmax[1] = SC_MIN (innerminmax[1], minmax[1]);
+
+      length -= SC_MAX (0., innerminmax[1] - innerminmax[0]);
+    }
+
+    if (length > 0.) {
+      tsg->ray_lengths[ray->lidx] += length;
+      return 1;
+    }
+
+    /* the return value is irrelevant for leaves */
+    return 0;
+  }
+}
+
+static void
+time_search_1 (tsearch_global_t * tsg, p4est_t * p4est, size_t znum_points,
+               sc_flopinfo_t * fi, sc_statinfo_t * stats)
+{
+  const double        expected = tsg->expected;
+  int                 mpiret;
+  long long           ll, gg;
+  size_t              zz;
+  sc_array_t          pview;
+  sc_flopinfo_t       snapshot;
+
+  /*
+   * Search all points separately.
+   *
+   * For each point, perform a search to compute the local number of the
+   * containing quadrant and the reference coordinates in [0, 1]^d relative to
+   * this quadrant in-place.  This only happens if the quadrant is
+   * processor-local.
+   *
+   * The points are identical on all processors.  Due to points on quadrant
+   * boundaries, we will find slightly more points than we expect.
+   */
+
+  if (tsg->test_rays) {
+    memset (tsg->ray_lengths, 0, znum_points * sizeof (double));
+  }
+
+  ll = 0;
+  sc_flops_snap (fi, &snapshot);
+  for (zz = 0; zz < znum_points; ++zz) {
+    tsg->matches = 0;
+    sc_array_init_view (&pview, tsg->points, zz, 1);
+    p4est_search (p4est, time_search_fn, time_search_fn, &pview);
+    ll += (long long) tsg->matches;
+  }
+  sc_flops_shot (fi, &snapshot);
+  sc_stats_set1 (&stats[TSEARCH_SEARCH_1], snapshot.iwtime, "Search_1");
+
+  if (!tsg->test_rays) {
+    mpiret = sc_MPI_Allreduce (&ll, &gg, 1, sc_MPI_LONG_LONG_INT, sc_MPI_SUM,
+                               tsg->mpicomm);
+    SC_CHECK_MPI (mpiret);
+
+    P4EST_GLOBAL_STATISTICSF
+      ("Search_1 expected %lld found %lld of %lld error %.3g%%\n",
+       (long long) round (expected), gg, (long long) znum_points,
+       100. * fabs ((gg - expected) / expected));
+  }
+  else {
+    double             *ray_lengths_buf = NULL;
+    sc_statinfo_t       raystat;
+
+    if (!p4est->mpirank) {
+      ray_lengths_buf = P4EST_ALLOC_ZERO (double, znum_points);
+    }
+
+    mpiret = sc_MPI_Reduce (tsg->ray_lengths, ray_lengths_buf,
+                            (int) znum_points, sc_MPI_DOUBLE, sc_MPI_SUM, 0,
+                            p4est->mpicomm);
+    SC_CHECK_MPI (mpiret);
+
+    sc_stats_init (&raystat, "Search_1 ray length errors");
+
+    if (!p4est->mpirank) {
+      for (zz = 0; zz < znum_points; zz++) {
+        double              diff;
+
+        diff = ray_lengths_buf[zz] - tsg->ray_lengths_expected[zz];
+        sc_stats_accumulate (&raystat, diff);
+      }
+
+      P4EST_FREE (ray_lengths_buf);
+    }
+
+    sc_stats_compute (tsg->mpicomm, 1, &raystat);
+    sc_stats_print (p4est_package_id, SC_LP_STATISTICS, 1, &raystat, 1, 0);
+  }
+}
+
+static void
+time_search_N (tsearch_global_t * tsg, p4est_t * p4est, size_t znum_points,
+               sc_flopinfo_t * fi, sc_statinfo_t * stats)
+{
+  const double        expected = tsg->expected;
+  int                 mpiret;
+  long long           ll, gg;
+  sc_flopinfo_t       snapshot;
+
+  /*
+   * Search all points in one pass through the forest
+   *
+   * For each point, perform a search to compute the local number of the
+   * containing quadrant and the reference coordinates in [0, 1]^d relative to
+   * this quadrant in-place.  This only happens if the quadrant is
+   * processor-local.
+   *
+   * The points are identical on all processors.  Due to points on quadrant
+   * boundaries, we will find slightly more points than we expect.
+   */
+
+  if (tsg->test_rays) {
+    memset (tsg->ray_lengths, 0, znum_points * sizeof (double));
+  }
+
+  tsg->matches = 0;
+  sc_flops_snap (fi, &snapshot);
+  p4est_search (p4est, time_search_fn, time_search_fn, tsg->points);
+  sc_flops_shot (fi, &snapshot);
+  sc_stats_set1 (&stats[TSEARCH_SEARCH_N], snapshot.iwtime, "Search_N");
+  ll = (long long) tsg->matches;
+
+  if (!tsg->test_rays) {
+    mpiret = sc_MPI_Allreduce (&ll, &gg, 1, sc_MPI_LONG_LONG_INT, sc_MPI_SUM,
+                               tsg->mpicomm);
+    SC_CHECK_MPI (mpiret);
+
+    P4EST_GLOBAL_STATISTICSF
+      ("Search_N expected %lld found %lld of %lld error %.3g%%\n",
+       (long long) round (expected), gg, (long long) znum_points,
+       100. * fabs ((gg - expected) / expected));
+  }
+  else {
+    double             *ray_lengths_buf = NULL;
+    sc_statinfo_t       raystat;
+
+    if (!p4est->mpirank) {
+      ray_lengths_buf = P4EST_ALLOC_ZERO (double, znum_points);
+    }
+
+    mpiret = sc_MPI_Reduce (tsg->ray_lengths, ray_lengths_buf,
+                            (int) znum_points, sc_MPI_DOUBLE, sc_MPI_SUM, 0,
+                            p4est->mpicomm);
+    SC_CHECK_MPI (mpiret);
+
+    sc_stats_init (&raystat, "Search_N ray length errors");
+
+    if (!p4est->mpirank) {
+      size_t              zz;
+
+      for (zz = 0; zz < znum_points; zz++) {
+        double              diff;
+
+        diff = ray_lengths_buf[zz] - tsg->ray_lengths_expected[zz];
+        sc_stats_accumulate (&raystat, diff);
+      }
+
+      P4EST_FREE (ray_lengths_buf);
+    }
+
+    sc_stats_compute (tsg->mpicomm, 1, &raystat);
+    sc_stats_print (p4est_package_id, SC_LP_STATISTICS, 1, &raystat, 1, 0);
+  }
+}
+
+static void
+time_search_all (tsearch_global_t * tsg, p4est_t * p4est, size_t znum_points,
+                 sc_flopinfo_t * fi, sc_statinfo_t * stats)
+{
+  int                 i;
+  double              ratio;
+  size_t              zz;
+  tsearch_point_t    *point;
+  ts_ray_t           *ray;
+
+  if (!tsg->test_rays) {
+    tsg->points = sc_array_new_size (sizeof (tsearch_point_t), znum_points);
+    for (zz = 0; zz < znum_points; ++zz) {
+      point = (tsearch_point_t *) sc_array_index (tsg->points, zz);
+      for (i = 0; i < P4EST_DIM; ++i) {
+        point->xy[i] = 2. * (rand () / (RAND_MAX + 1.)) - 1.;
+      }
+      point->lid = -1;
+    }
+
+    ratio = 4. / 3. * M_PI * (pow (tsg->rout, 3.) - pow (tsg->rin, 3.)) / 8.;
+    tsg->expected = ratio * znum_points;
+  }
+  else {
+    tsg->points = sc_array_new_size (sizeof (ts_ray_t), znum_points);
+    tsg->ray_lengths = P4EST_ALLOC (double, znum_points);
+    tsg->ray_lengths_expected = P4EST_ALLOC (double, znum_points);
+    for (zz = 0; zz < znum_points; ++zz) {
+      double              theta, sintheta, costheta, sinphi, cosphi, r;
+      double              x[3], y[3], d[3];
+      double              minmax[2];
+
+      ray = (ts_ray_t *) sc_array_index (tsg->points, zz);
+
+      /* generate a random direction */
+      theta = 2. * M_PI * ((double) rand () / (double) (RAND_MAX));
+      sintheta = sin (theta);
+      costheta = cos (theta);
+
+      sinphi = 2. * ((double) rand () / (double) (RAND_MAX)) - 1.;
+      cosphi = sqrt (1. - sinphi * sinphi);
+
+      ray->dir[0] = d[0] = costheta * cosphi;
+      ray->dir[1] = d[1] = sintheta * cosphi;
+      ray->dir[2] = d[2] = sinphi;
+      ray->idir[0] = 1. / d[0];
+      ray->idir[1] = 1. / d[1];
+      ray->idir[2] = 1. / d[2];
+
+      /* get a basis for the plane orthogonal to the direction */
+      if (d[0] * d[0] > d[1] * d[1] && d[0] * d[0] > d[2] * d[2]) {
+        x[0] = d[1];
+        x[1] = -d[0];
+        x[2] = 0.;
+      }
+      else {
+        x[0] = 0.;
+        x[1] = d[2];
+        x[2] = -d[1];
+      }
+
+      r = x[0] * x[0] + x[1] * x[1] + x[2] * x[2];
+      r = sqrt (r);
+      x[0] /= r;
+      x[1] /= r;
+      x[2] /= r;
+
+      y[0] = d[1] * x[2] - d[2] * x[1];
+      y[1] = d[2] * x[0] - d[0] * x[2];
+      y[2] = d[0] * x[1] - d[1] * x[0];
+
+      /* generate a random point in the circle orthogonal to the direction */
+      theta = 2. * M_PI * ((double) rand () / (double) (RAND_MAX));
+      sintheta = sin (theta);
+      costheta = cos (theta);
+      r =
+        ((double) rand () / (double) (RAND_MAX)) +
+        ((double) rand () / (double) (RAND_MAX));
+      if (r > 1.) {
+        r = 2. - r;
+      }
+      r *= tsg->rout;
+
+      ray->off[0] = x[0] * r * costheta + y[0] * r * sintheta;
+      ray->off[1] = x[1] * r * costheta + y[1] * r * sintheta;
+      ray->off[2] = x[2] * r * costheta + y[2] * r * sintheta;
+      ray->off2 = r * r;
+
+      ray->lidx = (p4est_locidx_t) zz;
+
+      if (ts_ray_sphere_test (ray, tsg->rout * tsg->rout, minmax)) {
+        double              length = minmax[1] - minmax[0];
+        double              iminmax[2];
+
+        if (ts_ray_sphere_test (ray, tsg->rin * tsg->rin, iminmax)) {
+          length -= (iminmax[1] - iminmax[0]);
+        }
+        tsg->ray_lengths_expected[zz] = length;
+      }
+      else {
+        tsg->ray_lengths_expected[zz] = 0.;
+      }
+    }
+  }
+
+  if (!tsg->skip_1) {
+    time_search_1 (tsg, p4est, znum_points, fi, stats);
+  }
+  else {
+    sc_stats_set1 (&stats[TSEARCH_SEARCH_1], 0., "Search_1");
+  }
+  time_search_N (tsg, p4est, znum_points, fi, stats);
+
+  sc_array_destroy (tsg->points);
+  if (tsg->test_rays) {
+    P4EST_FREE (tsg->ray_lengths);
+    P4EST_FREE (tsg->ray_lengths_expected);
+  }
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 first_argc;
+  int                 refine_local;
+  int                 write_vtk;
+  int                 test_rays;
+  size_t              znum_points;
+  unsigned            crc;
+  p4est_gloidx_t      count_refined, count_balanced;
+  p4est_connectivity_t *connectivity;
+  p4est_t            *p4est;
+  sc_statinfo_t       stats[TSEARCH_NUM_STATS];
+  sc_flopinfo_t       fi, snapshot;
+  sc_options_t       *opt;
+  tsearch_global_t    tsgt, *tsg = &tsgt;
+  int                 skip_1;
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  tsg->mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (tsg->mpicomm, &tsg->mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (tsg->mpicomm, &tsg->mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  /* initialize p4est internals */
+  sc_init (tsg->mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+#ifndef P4EST_ENABLE_DEBUG
+  sc_set_log_defaults (NULL, NULL, SC_LP_STATISTICS);
+#endif
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* initialize global data */
+  srand (tsg->mpisize);
+  tsg->rout = 1.;
+  tsg->rin = .55;
+  tsg->rout2 = tsg->rout * tsg->rout;
+  tsg->rin2 = tsg->rin * tsg->rin;
+  tsg->routbyrin = tsg->rout / tsg->rin;
+  tsg->logrbyr = log (tsg->routbyrin);
+
+  /* process command line arguments */
+  opt = sc_options_new (argv[0]);
+  sc_options_add_int (opt, 'l', "level", &refine_level, 0,
+                      "Refinement level");
+  sc_options_add_int (opt, 's', "level-shift", &level_shift, 4,
+                      "Refinement shift");
+  sc_options_add_bool (opt, 'L', "refine-local", &refine_local, 0,
+                       "Refine around one point");
+  sc_options_add_bool (opt, 'V', "write-vtk", &write_vtk, 0,
+                       "Write VTK files");
+  sc_options_add_size_t (opt, 'N', "num-points", &znum_points, 0,
+                         "Number of points");
+  sc_options_add_switch (opt, 'r', "rays", &test_rays,
+                         "Test rays instead of points");
+  sc_options_add_switch (opt, 'm', "skip-1", &skip_1,
+                         "only test parallelized search");
+  first_argc = sc_options_parse (p4est_package_id, SC_LP_ERROR,
+                                 opt, argc, argv);
+  if (first_argc < 0 || first_argc != argc) {
+    sc_options_print_usage (p4est_package_id, SC_LP_ERROR, opt, NULL);
+    return 1;
+  }
+  sc_options_print_summary (p4est_package_id, SC_LP_PRODUCTION, opt);
+
+  tsg->test_rays = test_rays;
+  tsg->skip_1 = skip_1;
+
+  /* start overall timing */
+  mpiret = sc_MPI_Barrier (tsg->mpicomm);
+  SC_CHECK_MPI (mpiret);
+  sc_flops_start (&fi);
+
+  /* create connectivity and forest */
+  sc_flops_snap (&fi, &snapshot);
+  connectivity = p8est_connectivity_new_shell ();
+  p4est = p4est_new_ext (tsg->mpicomm, connectivity,
+                         0, refine_level - level_shift, 1, 0, NULL, tsg);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TSEARCH_NEW], snapshot.iwtime, "New");
+  if (write_vtk) {
+    p4est_vtk_write_file (p4est, NULL, "tsearch_new");
+  }
+
+  /* time refine */
+  sc_flops_snap (&fi, &snapshot);
+  p4est_refine (p4est, 1,
+                refine_local ? refine_local_fn : refine_fractal_fn, NULL);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TSEARCH_REFINE], snapshot.iwtime, "Refine");
+  if (write_vtk) {
+    p4est_vtk_write_file (p4est, NULL, "tsearch_refined");
+  }
+  count_refined = p4est->global_num_quadrants;
+
+  /* time balance */
+  sc_flops_snap (&fi, &snapshot);
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TSEARCH_BALANCE], snapshot.iwtime, "Balance");
+  if (write_vtk) {
+    p4est_vtk_write_file (p4est, NULL, "tsearch_balanced");
+  }
+  count_balanced = p4est->global_num_quadrants;
+  crc = p4est_checksum (p4est);
+
+  /* time a uniform partition */
+  sc_flops_snap (&fi, &snapshot);
+  p4est_partition (p4est, 0, NULL);
+  sc_flops_shot (&fi, &snapshot);
+  sc_stats_set1 (&stats[TSEARCH_PARTITION], snapshot.iwtime, "Partition");
+  if (write_vtk) {
+    p4est_vtk_write_file (p4est, NULL, "tsearch_partitioned");
+  }
+  P4EST_ASSERT (crc == p4est_checksum (p4est));
+
+  /* run search timings */
+  time_search_all (tsg, p4est, znum_points, &fi, stats);
+
+  /* print status and checksum */
+  P4EST_GLOBAL_STATISTICSF
+    ("Processors %d level %d shift %d points %llu checksum 0x%08x\n",
+     tsg->mpisize, refine_level, level_shift,
+     (unsigned long long) znum_points, crc);
+  P4EST_GLOBAL_STATISTICSF ("Level %d refined to %lld balanced to %lld\n",
+                            refine_level, (long long) count_refined,
+                            (long long) count_balanced);
+
+  /* calculate and print timings */
+  sc_stats_compute (tsg->mpicomm, TSEARCH_NUM_STATS, stats);
+  sc_stats_print (p4est_package_id, SC_LP_STATISTICS,
+                  TSEARCH_NUM_STATS, stats, 1, 1);
+
+  /* destroy the p4est and its connectivity structure */
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (connectivity);
+
+  /* clean up and exit */
+  sc_options_destroy (opt);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/example/timings/tsrana.awk b/example/timings/tsrana.awk
new file mode 100755
index 0000000..0ab2289
--- /dev/null
+++ b/example/timings/tsrana.awk
@@ -0,0 +1,29 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+	print "# procs level shift refinelocal elements points Search_1 Search_N"
+}
+/refine-local:/ {
+	if ($3 == "true") { refinelocal = 1 } else {refinelocal = 0 };
+}
+/New p(4|8)est/ {
+	if ($3 == "p4est") { dim = 2 }
+	else if ($3 == "p8est") { dim = 3 }
+	#print "We are in dimension " dim
+}
+/^\[p4est\] Processors [[:digit:]]+ level/ {
+	procs = $3
+	level = $5
+	shift = $7
+	points = $9
+	elems = 0
+	#printf "Procs %d level %d shift %d points %d\n", procs, level, shift, points
+}
+/^\[p4est\] .* balanced to/ {
+	elems = $9
+}
+/^\[p4est\] Summary =/ {
+	# Output runtimes for: Search_1 Search_N
+	printf "%d %d %d %d %d %d %g %g\n",
+	       procs, level, shift, refinelocal, elems, points, $9, $10
+}
diff --git a/example/timings/tsrana.sh b/example/timings/tsrana.sh
new file mode 100755
index 0000000..619fe13
--- /dev/null
+++ b/example/timings/tsrana.sh
@@ -0,0 +1,66 @@
+#! /bin/sh
+
+# Pipe in the contents of one or more log files written by p8est_tsearch
+# We create three plots: one each for uniform, fractal, and localized ref.
+
+# Find path for the content filter script
+TSRANAWK=`echo $0 | sed -e 's/\.sh$/.awk/'`
+if test ! -x "$TSRANAWK" ; then
+	echo "No executable $TSRANAWK"
+	exit 1
+fi
+
+#TMPF=tsrana.tmp
+TMPF=`tempfile --prefix=tsrana`
+#echo "Temp file: $TMPF"
+
+$TSRANAWK $@ > $TMPF
+
+gnuplot <<EOF
+
+set term postscript color
+
+set logscale xy
+set key left
+
+set xlabel "points / core"
+set ylabel "runtime [1s]"
+
+# Fields: procs level shift refinelocal elements points Search_1 Search_N
+
+set macros
+
+# the @ignore variable is set below (using lazy evaluation)"
+# except the replot command does not pick up the variable changes
+ign262 = "(@ignore || \$6 == 262144)"
+onl262 = "(@ignore || \$6 != 262144)"
+
+# ignore the runs with --refine-local altogether
+set output "tsrana-uniform.eps"
+set title "Uniform refinement"
+ignore = "(\$3 != 0 || \$4 != 0)"
+plot	"$TMPF" using (@ign262 ? 1/0 : \$6 / \$1):(\$7) title "Search_1", \
+	"$TMPF" using (@onl262 ? 1/0 : \$6 / \$1):(\$7) title "Search_1, P=262144", \
+	"$TMPF" using (@ignore ? 1/0 : \$6 / \$1):(\$8) title "Search_N", \
+	1e-3 * x title "linear scaling" with lines, \
+	1 title "1 second"
+
+set output "tsrana-fractal.eps"
+set title "Fractal refinement"
+ignore = "(\$3 != 4 || \$4 != 0)"
+plot	"$TMPF" using (@ign262 ? 1/0 : \$6 / \$1):(\$7) title "Search_1", \
+	"$TMPF" using (@onl262 ? 1/0 : \$6 / \$1):(\$7) title "Search_1, P=262144", \
+	"$TMPF" using (@ignore ? 1/0 : \$6 / \$1):(\$8) title "Search_N", \
+	1e-3 * x title "linear scaling" with lines, \
+	1 title "1 second"
+
+set output "tsrana-local.eps"
+set title "Localized refinement"
+ignore = "(\$3 != 4 || \$4 != 1)"
+plot	"$TMPF" using (@ign262 ? 1/0 : \$6 / \$1):(\$7) title "Search_1", \
+	"$TMPF" using (@onl262 ? 1/0 : \$6 / \$1):(\$7) title "Search_1, P=262144", \
+	"$TMPF" using (@ignore ? 1/0 : \$6 / \$1):(\$8) title "Search_N", \
+	1e-3 * x title "linear scaling" with lines, \
+	1 title "1 second"
+
+EOF
diff --git a/p4estindent b/p4estindent
new file mode 100755
index 0000000..b9b2b09
--- /dev/null
+++ b/p4estindent
@@ -0,0 +1,40 @@
+#! /bin/bash
+
+# This is the GNU style for indent 2.2.9 (according to man page)
+#
+#"$INDENT" \
+#    -nbad -bap -nbc -bbo -bl -bli2 -bls -ncdb -nce -cp1 -cs -di2 \
+#    -ndj -nfc1 -nfca -hnl -i2 -ip5 -lp -pcs -nprs -psl -saf -sai \
+#    -saw -nsc -nsob
+#    "$@"
+
+# This is our modification
+#
+# blank line after procedure body
+# braces indent 0 (or do you want -bl2 here and -bl below?)
+# braces to right of control statements (or do you want -bl here?)
+# no tabs
+# put the return type of a function on a separate line
+# swallow optional blank lines
+INDENT_OPTIONS="-bap -bli0 -br -nut -psl -sob -di20"
+
+INDENT=`which gnuindent 2> /dev/null`
+if test -z "$INDENT" ; then
+	INDENT=`which gindent`
+fi
+if test -z "$INDENT" ; then
+	INDENT=`which indent`
+fi
+
+for arg in "$@" ; do
+  if [ "x$arg" == "x-o" ]; then
+    WANTSOUT=1
+  fi
+done
+if [ -z "$WANTSOUT" ]; then
+  for NAME in "$@" ; do
+    $INDENT $INDENT_OPTIONS "$NAME"
+  done
+else
+  $INDENT $INDENT_OPTIONS $@
+fi
diff --git a/sc/AUTHORS b/sc/AUTHORS
new file mode 100644
index 0000000..71cec34
--- /dev/null
+++ b/sc/AUTHORS
@@ -0,0 +1,88 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+/*
+  Authors:
+
+  Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  Lucas C. Wilcox <lucasw at ices.utexas.edu>
+  Tobin Isaac <tisaac at ices.utexas.edu>
+*/
+
+/*
+  Contributors:
+
+  James Martin <jmartin at ices.utexas.edu>
+  Tan Bui-Thanh <tanbui at ices.utexas.edu>
+  Adrian Seyboldt <adrian.seyboldt at gmail.com>
+  Sarah Weischer <s.weischer at gmx.de>
+  Johannes Holke <holke at ins.uni-bonn.de>
+*/
+
+/*
+  Thanks to:
+
+  Matthias Maier <matthias.maier at iwr.uni-heidelberg.de>
+*/
+
+/*
+  Acknowledgement and Disclaimer:
+
+  The development of libsc was partially supported by the US National Science
+  Foundation (NSF Grants No. OCI-0749334, CCF-0427985, CNS-0540372,
+  CNS-0619838, DMS-0724746, OPP-0941678) and the US Department of Energy (DOE
+  Grants No.  06ER25782, 08ER25860, SC0002710).  The authors thank the Texas
+  Advanced Computing Center (TACC) for providing them with access to the Ranger
+  supercomputer under NSF TeraGrid award MCA04N026, and the National Center for
+  Computational Science (NCCS) for early-user access to the Jaguar Cray XT5
+  supercomputer.  Any opinions, findings and conclusions or recomendations
+  expressed in the source code and documentation are those of the authors and
+  do not necessarily reflect the views of the National Science Foundation
+  (NSF).
+*/
+
+/*
+  The files under src/sc_builtin are copied from the GNU C library.
+  See copyright information below.
+
+  The files sc_avl.{c,h} are derived from libavl (Debian 0.3.5-2),
+  Copyright (C) 1998  Michael H. Buselli <cosine at cosine.org>
+  Copyright (C) 2000-2002  Wessel Dankers <wsl at nl.linux.org>.
+
+  The files sc_obstack.c and sc_getopt*.c are derived from the GNU C
+  library 2.7, Copyright (C) Free Software Foundation, Inc.
+
+  The source files under libb64 are derived from libb64.  libb64 is public
+  domain software, see the LICENSE and AUTHORS files in that directory.
+
+  The files under iniparser are derived from iniparser3.0b.  iniparser is
+  licensed unter the MIT license, see the LICENSE and AUTHORS files in
+  that directory.
+
+  Several scripts under config are released under GNU GPL versions.
+  These are not compiled or linked with libsc, so we consider them
+  independent programs that are not part of libsc.
+
+  Thanks to Rahul Sampath for mentioning the bitonic sort algorithm and to
+  www.inf.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/bitonicen.htm
+  for a nice description of the serial special case for arbitrary n.
+*/
diff --git a/sc/COPYING b/sc/COPYING
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/sc/COPYING
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+

+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+

+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+

+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+

+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+

+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+

+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+

+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+

+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+

+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey 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 library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/sc/Doxyfile.in b/sc/Doxyfile.in
new file mode 100644
index 0000000..da6d7cb
--- /dev/null
+++ b/sc/Doxyfile.in
@@ -0,0 +1,2305 @@
+# Doxyfile 1.8.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = @PACKAGE_NAME@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = @top_builddir@/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        = @top_srcdir@
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = @top_srcdir@/src \
+                         @top_srcdir@/doc/mainpage.dox
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        = SC_EXTERN_C_BEGIN \
+                         SC_EXTERN_C_END
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 3
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          = sc_
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 150
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = @PACKAGE_PREFIX at _DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       = @top_builddir@/doxygen/@PACKAGE_NAME at .doxygen.tags
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/sc/INSTALL b/sc/INSTALL
new file mode 100644
index 0000000..8b82ade
--- /dev/null
+++ b/sc/INSTALL
@@ -0,0 +1,291 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007, 2008 Free Software Foundation, Inc.
+
+   This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+   Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package.  The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.
+
+     Running `configure' might take a while.  While running, it prints
+     some messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+  6. Often, you can also type `make uninstall' to remove the installed
+     files again.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you can use GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory.  After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+   On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor.  Like
+this:
+
+     ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CPP="gcc -E" CXXCPP="g++ -E"
+
+   This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
+
+Installation Names
+==================
+
+   By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Particular systems
+==================
+
+   On HP-UX, the default C compiler is not ANSI C compatible.  If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+     ./configure CC="cc -Ae"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+   On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `<wchar.h>' header file.  The option `-nodtk' can be used as
+a workaround.  If GNU CC is not installed, it is therefore recommended
+to try
+
+     ./configure CC="cc"
+
+and if that doesn't work, try
+
+     ./configure CC="cc -nodtk"
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on.  Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+   Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug.  Until the bug is fixed you can use this workaround:
+
+     CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+     Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+     Print a summary of the options unique to this package's
+     `configure', and exit.  The `short' variant lists options used
+     only in the top level, while the `recursive' variant lists options
+     also present in any nested packages.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--prefix=DIR'
+     Use DIR as the installation prefix.  *Note Installation Names::
+     for more details, including other options available for fine-tuning
+     the installation locations.
+
+`--no-create'
+`-n'
+     Run the configure checks, but stop before creating any output
+     files.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/sc/Makefile.am b/sc/Makefile.am
new file mode 100644
index 0000000..c8c4463
--- /dev/null
+++ b/sc/Makefile.am
@@ -0,0 +1,115 @@
+
+# This file is part of the SC Library
+# Makefile.am in toplevel directory
+
+ACLOCAL_AMFLAGS = -I config
+
+# initialize empty variables
+AM_CPPFLAGS =
+CLEANFILES =
+DISTCLEANFILES =
+EXTRA_DIST =
+LDADD =
+LINT_CSOURCES =
+TESTS =
+bin_PROGRAMS =
+check_PROGRAMS =
+include_HEADERS =
+lib_LTLIBRARIES =
+nodist_include_HEADERS =
+noinst_HEADERS =
+noinst_PROGRAMS =
+sysconf_DATA =
+
+# use this if you want to link in libsc without autotools
+sysconf_DATA += Makefile.sc.mk
+CLEANFILES += Makefile.sc.mk
+Makefile.sc.mk : Makefile.sc.pre
+	cat $< | \
+        sed -e 's,{\(.*prefix\)},{sc_\1},g' \
+            -e 's,^\(.*prefix *=\),sc_\1,g' > $@
+
+# install libsc m4 macros in a dedicated directory
+scaclocaldir = $(datadir)/aclocal
+dist_scaclocal_DATA = \
+        config/sc_blas.m4 config/sc_builtin.m4 config/sc_c_check_flag.m4 \
+        config/sc_c_version.m4 config/sc_cuda.m4 config/sc_include.m4 \
+        config/sc_lapack.m4 config/sc_lint.m4 config/sc_mpi.m4 \
+        config/ax_prefix_config_h.m4 config/ax_split_version.m4 \
+        config/sc_package.m4 config/sc_trilinos.m4
+
+# install example .ini files in a dedicated directory
+scinidir = $(datadir)/ini
+dist_scini_DATA =
+
+# handle toplevel directory
+EXTRA_DIST += \
+        bootstrap scindent build-aux/git-version-gen build-aux/git2cl bugs doc
+DISTCLEANFILES += \
+        _configs.sed src/sc_config.h
+.PHONY: lint ChangeLog
+
+# setup test environment
+if SC_MPIRUN
+LOG_COMPILER = @SC_MPIRUN@
+AM_LOG_FLAGS = @SC_MPI_TEST_FLAGS@
+endif
+
+# non-recursive build
+include src/Makefile.am
+include iniparser/Makefile.am
+include libb64/Makefile.am
+include test/Makefile.am
+include example/bspline/Makefile.am
+## include example/cuda/Makefile.am
+include example/dmatrix/Makefile.am
+include example/function/Makefile.am
+include example/logging/Makefile.am
+include example/options/Makefile.am
+include example/pthread/Makefile.am
+include example/warp/Makefile.am
+
+# lint static syntax checker
+ALL_LINT_FLAGS = $(LINT_FLAGS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+                 $(MPI_INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) \
+                 $(src_libsc_a_CPPFLAGS)
+lint:
+if LINT
+	for f in $(LINT_CSOURCES) ; do \
+		$(LINT) $(ALL_LINT_FLAGS) $(top_srcdir)/$$f || \
+		echo "Lint check failed for $$f" ; \
+	done
+else
+	@echo "Static source code check disabled by configure"
+endif
+
+# revision control and ChangeLog
+ChangeLog:
+	(GIT_DIR=@top_srcdir@/.git git log > .ChangeLog.tmp && \
+         cat .ChangeLog.tmp | @top_srcdir@/build-aux/git2cl > ChangeLog) ; \
+        rm -f .ChangeLog.tmp
+
+dist-hook:
+	echo $(VERSION) > $(distdir)/.tarball-version
+	test "x$(VERSION)" = "x`@top_srcdir@/build-aux/git-version-gen\
+              @top_srcdir@/.tarball-version`" || \
+        ((echo "Stale version;" ; echo "Please run:" ; \
+          echo "     cd @top_srcdir@ && ./bootstrap" ; \
+          echo "before make dist") 1>&2 ; rm -r $(distdir) ; exit 1)
+
+clean-local:
+	rm -f ChangeLog
+
+maintainer-clean-local:
+	rm -rf doxygen
+
+doxygen: Doxyfile
+	doxygen
+
+.PHONY: doxygen
+
+## # CUDA support
+## if SC_WITH_CUDA
+## .cu.o:
+## 	$(SC_NVCC) -o $@ -c $< @NVCCFLAGS@
+## endif
diff --git a/sc/Makefile.sc.pre.in b/sc/Makefile.sc.pre.in
new file mode 100644
index 0000000..9bc3598
--- /dev/null
+++ b/sc/Makefile.sc.pre.in
@@ -0,0 +1,22 @@
+
+# This file is part of the SC Library
+# Use `include /path/to/Makefile.sc.mk' in your Makefile
+# to use libsc in your project without autotools
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+sc_sysconfdir = @sysconfdir@
+
+# SC_CC and SC_CFLAGS may not be necessary for your project
+SC_CC = @CC@
+SC_CFLAGS = @CFLAGS@
+
+# These pull in sc but none of its dependencies
+SC_PKG_CPPFLAGS = -I at includedir@
+SC_PKG_LDFLAGS = -L at libdir@
+SC_PKG_LIBS = -lsc
+
+# These pull in everything needed by libsc
+SC_CPPFLAGS = @CPPFLAGS@ $(SC_PKG_CPPFLAGS)
+SC_LDFLAGS = @LDFLAGS@ $(SC_PKG_LDFLAGS)
+SC_LIBS = $(SC_PKG_LIBS) @LIBS@
diff --git a/sc/NEWS b/sc/NEWS
new file mode 100644
index 0000000..6134374
--- /dev/null
+++ b/sc/NEWS
@@ -0,0 +1,4 @@
+The SC Library is now free software
+under the GNU Lesser General Public License version 2.1 (or later).
+
+See the files COPYING and AUTHORS for details.
diff --git a/sc/README b/sc/README
new file mode 100644
index 0000000..4f29ab4
--- /dev/null
+++ b/sc/README
@@ -0,0 +1,14 @@
+
+This is the README file for libsc.
+
+The SC Library provides support for parallel scientific applications.
+
+Copyright (C) 2010 The University of Texas System.
+libsc is written by Carsten Burstedde, Lucas C. Wilcox, Tobin Isaac, and others
+and released under the GNU Lesser General Public Licence version 2.1 (or, at
+your option, any later version).
+
+The official web page for source code and documentation is www.p4est.org.
+Please send bug reports and ideas for contribution to p4est at librelist.com.
+
+The build instructions for p4est also apply to standalone builds of libsc.
diff --git a/sc/bootstrap b/sc/bootstrap
new file mode 100755
index 0000000..eef9ffa
--- /dev/null
+++ b/sc/bootstrap
@@ -0,0 +1,17 @@
+#! /bin/sh
+
+echo "--- This is the bootstrap script for libsc ---"
+echo "It is NOT required to run bootstrap to build from a tar.gz archive"
+echo "Development requires a libtool recent enough to contain LT_INIT" \
+    "(2.2 works)"
+echo "Current directory is $PWD"
+
+LIBTOOLIZE=`which glibtoolize`
+if test ! -x "$LIBTOOLIZE" ; then LIBTOOLIZE=`which libtoolize` ; fi
+if test ! -x "$LIBTOOLIZE" ; then echo "bootstrap requires libtoolize" ; exit 1 ; fi
+
+aclocal -Wall -I config
+autoconf -Wall --force
+autoheader -Wall --force
+"$LIBTOOLIZE" --install --copy
+automake -Wall --add-missing --copy
diff --git a/sc/bugs/issue-04ae88702a9f6d21e8be9af5f12b70dc25d05b17.yaml b/sc/bugs/issue-04ae88702a9f6d21e8be9af5f12b70dc25d05b17.yaml
new file mode 100644
index 0000000..166265c
--- /dev/null
+++ b/sc/bugs/issue-04ae88702a9f6d21e8be9af5f12b70dc25d05b17.yaml
@@ -0,0 +1,31 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Make option callback more flexible
+desc: |-
+  Right now the callback function does not know the option name.
+  Also the option argument is neither stored, loaded or saved.
+  Change this so that it can e.g. easily emulate a string option.
+type: :bugfix
+component: libsc
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-12-04 04:36:38.086378 Z
+references: []
+
+id: 04ae88702a9f6d21e8be9af5f12b70dc25d05b17
+log_events: 
+- - 2009-12-04 04:36:39.870061 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2010-01-22 17:33:58.809291 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 1.6
+  - ""
+- - 2011-04-20 00:18:03.423353 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - |-
+    Callback load/save now uses the string value.
+    The user_data can be used to inform the callback of its context.
diff --git a/sc/bugs/issue-0abee8bb980e793066eac95db0b2c5ac946ce949.yaml b/sc/bugs/issue-0abee8bb980e793066eac95db0b2c5ac946ce949.yaml
new file mode 100644
index 0000000..a26f99e
--- /dev/null
+++ b/sc/bugs/issue-0abee8bb980e793066eac95db0b2c5ac946ce949.yaml
@@ -0,0 +1,33 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Improve abort and signal semantics
+desc: |-
+  1. Provide a configure option to disable the print of backtrace.
+  2. Don't catch signals by default, use a special function for that.
+type: :bugfix
+component: libsc
+release: "1.6"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-11-10 21:44:59.046839 Z
+references: []
+
+id: 0abee8bb980e793066eac95db0b2c5ac946ce949
+log_events: 
+- - 2008-11-10 21:45:00.406691 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-02-04 00:45:21.011848 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - |-
+    Remodeled sc_init. This breaks the API. Please update.
+    It takes a communicator argument now. If this is MPI_COMM_NULL,
+    the identifier (rank) is set to -1. Otherwise, the communicator
+    is stored for later use by sc_generic_abort_handler, which is
+    set as the abort handler, and the rank is retrieved and stored
+    as identifier.
+    The next two boolean arguments specify if the signals
+    INT, SEGV and USR2 are caught and trigger an sc_abort,
+    and if a backtrace is printed from within sc_abort.
diff --git a/sc/bugs/issue-2b229897e97d73ad3313b39f5043221e399996f1.yaml b/sc/bugs/issue-2b229897e97d73ad3313b39f5043221e399996f1.yaml
new file mode 100644
index 0000000..30bf9e1
--- /dev/null
+++ b/sc/bugs/issue-2b229897e97d73ad3313b39f5043221e399996f1.yaml
@@ -0,0 +1,37 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Fix parallel sort.
+desc: |-
+  The bitonic sort algorithm has a problem.  It fails randomly
+  which can be reproduced with mpirun -np 3 test/sc_test_sortb.
+type: :bugfix
+component: libsc
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-11-29 21:35:03.611981 Z
+references: []
+
+id: 2b229897e97d73ad3313b39f5043221e399996f1
+log_events: 
+- - 2008-11-29 21:35:04.235840 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-02-03 22:31:42.534180 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 1.6
+  - |-
+    Parallel sort is still buggy. Fix it for 0.7 hopefully.
+    Remember to remove the SC_DEBUG checks from test_sort.c and sc_sort.c
+    and to update documentation in sc_sort.h once the bug is fixed.
+- - 2013-07-03 13:42:44.561485 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    The sc_sort function has been fixed somewhat, but test_sc_sort is still
+    not happy for np >= 18.
+- - 2014-07-01 12:39:57.935188 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition fixed
+  - The test passes now for np = 18.  This does not mean the function is fast.
diff --git a/sc/bugs/issue-307055f1e9a4ef01d09267aae4e41d62b894606b.yaml b/sc/bugs/issue-307055f1e9a4ef01d09267aae4e41d62b894606b.yaml
new file mode 100644
index 0000000..bd974b5
--- /dev/null
+++ b/sc/bugs/issue-307055f1e9a4ef01d09267aae4e41d62b894606b.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Build Shared Libraries
+desc: |-
+  We should provide the option to build shared library version
+  of libsc.
+type: :feature
+component: libsc
+release: 
+reporter: Lucas C Wilcox <lucasw at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-07-28 21:41:03.783644 Z
+references: []
+
+id: 307055f1e9a4ef01d09267aae4e41d62b894606b
+log_events: 
+- - 2009-07-28 21:41:09.143433 Z
+  - Lucas C Wilcox <lucasw at ices.utexas.edu>
+  - created
+  - Initial commit
+- - 2009-11-19 19:00:11.266583 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - This seems to be working.
diff --git a/sc/bugs/issue-3a78f43954db1996755732df98db805415ab4ce8.yaml b/sc/bugs/issue-3a78f43954db1996755732df98db805415ab4ce8.yaml
new file mode 100644
index 0000000..e230a0b
--- /dev/null
+++ b/sc/bugs/issue-3a78f43954db1996755732df98db805415ab4ce8.yaml
@@ -0,0 +1,24 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Use libsc logging in sc_options
+desc: |-
+  Currently sc_options and some test/example programs print to stdout.
+  Use the SC_LOG and friends macros for output.
+type: :bugfix
+component: libsc
+release: "1.6"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-10-30 03:39:32.022184 Z
+references: []
+
+id: 3a78f43954db1996755732df98db805415ab4ce8
+log_events: 
+- - 2008-10-30 03:39:33.503538 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2008-11-09 20:08:43.719052 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - Done. Rename to sc_matrix_write because of the file pointer API.
diff --git a/sc/bugs/issue-498931301b13195e239d47941ee37086dc40cef6.yaml b/sc/bugs/issue-498931301b13195e239d47941ee37086dc40cef6.yaml
new file mode 100644
index 0000000..88a5494
--- /dev/null
+++ b/sc/bugs/issue-498931301b13195e239d47941ee37086dc40cef6.yaml
@@ -0,0 +1,31 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Add C++ C99 Compatibility Layer for Complex Arithmetic
+desc: |-
+  We should use the approach laid out in [1] to write complex arithmetic
+  code that can be compiled with C++ or C99.  To do this we would need to
+  only include a small header file [2] in libsc.  Carsten suggested that we
+  do not use abs for fabs and I do agree.  Note also that tgmath.h is a ISO
+  C standard header (aka C99) [3].
+  
+  [1] http://www.ddj.com/cpp/184401628?pgno=1
+  [2] http://www.ddj.com/cpp/184401628?pgno=2
+  [3] http://www.opengroup.org/onlinepubs/009695399/basedefs/tgmath.h.html
+type: :feature
+component: libsc
+release: 
+reporter: Lucas C Wilcox <lucasw at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-10-08 18:09:58.258892 Z
+references: []
+
+id: 498931301b13195e239d47941ee37086dc40cef6
+log_events: 
+- - 2009-10-08 18:10:03.410919 Z
+  - Lucas C Wilcox <lucasw at ices.utexas.edu>
+  - created
+  - Initial report
+- - 2009-10-08 18:42:50.628613 Z
+  - Lucas C Wilcox <lucasw at ices.utexas.edu>
+  - closed with disposition fixed
+  - Fixed
diff --git a/sc/bugs/issue-50a26b60f1fbcd5195940ca0d6af13a16c4b9c6d.yaml b/sc/bugs/issue-50a26b60f1fbcd5195940ca0d6af13a16c4b9c6d.yaml
new file mode 100644
index 0000000..1c694e6
--- /dev/null
+++ b/sc/bugs/issue-50a26b60f1fbcd5195940ca0d6af13a16c4b9c6d.yaml
@@ -0,0 +1,20 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Static syntax checkers
+desc: |-
+  The current lint/splint support is outdated and does not work.
+  We should probably reconsider the tools we support.
+type: :bugfix
+component: libsc
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :unstarted
+disposition: 
+creation_time: 2014-08-27 10:31:01.070640 Z
+references: []
+
+id: 50a26b60f1fbcd5195940ca0d6af13a16c4b9c6d
+log_events: 
+- - 2014-08-27 10:31:01.966370 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
diff --git a/sc/bugs/issue-5af792eb4766555f1204743970c5d6a3e5e1eaf2.yaml b/sc/bugs/issue-5af792eb4766555f1204743970c5d6a3e5e1eaf2.yaml
new file mode 100644
index 0000000..ff8cf36
--- /dev/null
+++ b/sc/bugs/issue-5af792eb4766555f1204743970c5d6a3e5e1eaf2.yaml
@@ -0,0 +1,20 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Remove const from hash user data
+desc: |-
+  The user_data of sc_hash_t in sc_containers is declared const.
+  This may be overly restrictive.  Remove this and clean up accordingly.
+type: :bugfix
+component: libsc
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2011-04-20 00:03:51.993552 Z
+references: []
+
+id: 5af792eb4766555f1204743970c5d6a3e5e1eaf2
+log_events: 
+- - 2011-04-20 00:03:53.154710 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
diff --git a/sc/bugs/issue-63fab9640a28e11bcebe98f9a18612aec9917cfa.yaml b/sc/bugs/issue-63fab9640a28e11bcebe98f9a18612aec9917cfa.yaml
new file mode 100644
index 0000000..5d4df60
--- /dev/null
+++ b/sc/bugs/issue-63fab9640a28e11bcebe98f9a18612aec9917cfa.yaml
@@ -0,0 +1,20 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Verify priority queue functions
+desc: |-
+  Check sc_containers for the pqueue functions.
+  Not sure if they are correct currently.
+type: :bugfix
+component: libsc
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2009-07-21 21:13:31.461136 Z
+references: []
+
+id: 63fab9640a28e11bcebe98f9a18612aec9917cfa
+log_events: 
+- - 2009-07-21 21:13:32.508893 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
diff --git a/sc/bugs/issue-7f4c9393e1e2f9d173c9633dd3e1a96b947f702a.yaml b/sc/bugs/issue-7f4c9393e1e2f9d173c9633dd3e1a96b947f702a.yaml
new file mode 100644
index 0000000..6b6d75f
--- /dev/null
+++ b/sc/bugs/issue-7f4c9393e1e2f9d173c9633dd3e1a96b947f702a.yaml
@@ -0,0 +1,35 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Add ENABLE or WITH to SC_ARG defines
+desc: |-
+  The SC_ARG_ENABLE and SC_ARG_WITH macres use AC_DEFINE
+  to define preprocessor macros.  Currently these macros
+  do not include the ENABLE or WITH tokens which makes them
+  different from the AC_CONDITIONALS which include them.
+  Beware that this will require changes in dependent projects.
+type: :bugfix
+component: libsc
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2010-07-17 19:55:37.908549 Z
+references: []
+
+id: 7f4c9393e1e2f9d173c9633dd3e1a96b947f702a
+log_events: 
+- - 2010-07-17 19:55:39.360433 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2014-02-11 11:17:49.662565 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    The ENABLE_ and DISABLE_ defines are in place.
+    The old ones are deprecated and need to be phased out in the code.
+- - 2014-02-11 11:26:20.998555 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    The AM_CONDITIONAL has been changed over.
+    Use of old conditionals will throw errors and force the fix.
diff --git a/sc/bugs/issue-82067d245dbf57b2a938b7c2f1e24ab49920737e.yaml b/sc/bugs/issue-82067d245dbf57b2a938b7c2f1e24ab49920737e.yaml
new file mode 100644
index 0000000..d57a1ee
--- /dev/null
+++ b/sc/bugs/issue-82067d245dbf57b2a938b7c2f1e24ab49920737e.yaml
@@ -0,0 +1,34 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Upgrade iniparser library to latest version
+desc: |-
+  There is version 3.1 on https://github.com/ndevilla/iniparser.
+  It still does not have getlong, but should be upgraded anyway.
+type: :bugfix
+component: libsc
+release: 
+reporter: Carsten Burstedde <burstedde at ins.uni-bonn.de>
+status: :closed
+disposition: :fixed
+creation_time: 2014-03-04 17:42:12.260299 Z
+references: []
+
+id: 82067d245dbf57b2a938b7c2f1e24ab49920737e
+log_events: 
+- - 2014-03-04 17:42:13.933059 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - created
+  - ""
+- - 2014-03-04 18:00:38.668209 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - We also should add range checks to sc_options.c.
+- - 2014-07-18 15:51:14.832663 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - commented
+  - |-
+    Iniparser is now at 3.1.
+    Range checks for double are still missing.
+- - 2014-07-21 08:54:28.765810 Z
+  - Carsten Burstedde <burstedde at ins.uni-bonn.de>
+  - closed with disposition fixed
+  - Some of these changes may benefit iniparser -- will see who has time.
diff --git a/sc/bugs/issue-a1aa51e81a6f7c92ad91376ce9150ce05496049c.yaml b/sc/bugs/issue-a1aa51e81a6f7c92ad91376ce9150ce05496049c.yaml
new file mode 100644
index 0000000..3858df5
--- /dev/null
+++ b/sc/bugs/issue-a1aa51e81a6f7c92ad91376ce9150ce05496049c.yaml
@@ -0,0 +1,26 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Improve sc_abort
+desc: |-
+  1. Make sc_abort use log functions for its output.
+  2. Provide a way of aborting a program with logging only on rank 0.
+type: :bugfix
+component: libsc
+release: "1.6"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-10-07 20:30:56.280039 Z
+references: []
+
+id: a1aa51e81a6f7c92ad91376ce9150ce05496049c
+log_events: 
+- - 2009-10-07 20:30:57.935925 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-11-19 21:46:02.140022 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - |-
+    Uses SC_LP_ERROR log level.
+    The functions sc_is_root and sc_abort_root have been added.
diff --git a/sc/bugs/issue-ba52b9a974843d380af0f183866ec47c43348bbe.yaml b/sc/bugs/issue-ba52b9a974843d380af0f183866ec47c43348bbe.yaml
new file mode 100644
index 0000000..1527a38
--- /dev/null
+++ b/sc/bugs/issue-ba52b9a974843d380af0f183866ec47c43348bbe.yaml
@@ -0,0 +1,26 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Isolate sc_keyvalue.{c,h} files
+desc: |-
+  The sc_object files contain sc_object_arguments.
+  This is a rather independent key-value-pair implementation.
+  Create a kev-value implementation in sc_keyvalue.h and .c
+  and remove the object_value logic from sc_object.
+type: :bugfix
+component: libsc
+release: "1.6"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-09-30 18:18:40.466307 Z
+references: []
+
+id: ba52b9a974843d380af0f183866ec47c43348bbe
+log_events: 
+- - 2009-09-30 18:18:42.314073 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-10-02 22:15:49.477995 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - James did that.
diff --git a/sc/bugs/issue-be85cecaeb979aee5dd659e2d4da4c06961aaea8.yaml b/sc/bugs/issue-be85cecaeb979aee5dd659e2d4da4c06961aaea8.yaml
new file mode 100644
index 0000000..23533c9
--- /dev/null
+++ b/sc/bugs/issue-be85cecaeb979aee5dd659e2d4da4c06961aaea8.yaml
@@ -0,0 +1,31 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Unify integration of builtin packages
+desc: |-
+  Currently getopt, obstack and zlib are optional builtin packages.
+  Still they are handled differently wrt. configure logic.
+  The ideal behaviour would be to have:
+  1. The exact same configure look and feel and
+  2. A reliable configure check to decide if the builtin version is used.
+type: :bugfix
+component: libsc
+release: "1.6"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2008-11-09 18:27:27.275961 Z
+references: []
+
+id: be85cecaeb979aee5dd659e2d4da4c06961aaea8
+log_events: 
+- - 2008-11-09 18:27:29.547963 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-02-03 00:00:07.493402 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - |-
+    Default action is to check for the package and use a builtin replacement
+    if the check fails.  If the --without-* option is given, no checks are
+    performed and the builtin code is not used.  In this case the user needs
+    to ensure presence of the package or the libsc will not compile or link.
diff --git a/sc/bugs/issue-d1c07123f50402055f9cc973e68872d7ed05fc7b.yaml b/sc/bugs/issue-d1c07123f50402055f9cc973e68872d7ed05fc7b.yaml
new file mode 100644
index 0000000..dfc9b3f
--- /dev/null
+++ b/sc/bugs/issue-d1c07123f50402055f9cc973e68872d7ed05fc7b.yaml
@@ -0,0 +1,56 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Fix warnings from llvm static analyzer
+desc: |-
+  The static analyzer[0] from llvm finds a couple of possible problems
+  with the code.
+  
+  I build libsc in the following way
+  
+    scan-build ./configure CFLAGS="-std=c99 -O0 -g -Wall -Wextra -Wshadow -Wno-unused-parameter" --enable-debug --with-p4est --with-lint
+    make
+  
+  and got the following warnings
+  
+    src/sc_options.c:499:13: warning: Dereference of null pointer.
+        switch (item->opt_type) {
+                ^~~~~~~~~~~~~~
+    src/sc_options.c:481:7: warning: Branch condition evaluates to an uninitialized value.
+          SC_ASSERT (item_index >= 0);
+          ^~~~~~~~~  
+    rc/sc_getopt.c:882:39: warning: Dereference of null pointer.
+            for (p = longopts, option_index = 0; p->name; p++, option_index++)
+                                                 ^~~~~~~
+    src/sc_obstack.c:164:35: warning: Dereference of null pointer.
+      h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
+                                      ^~~~~~~~~~~
+    ANALYZE: src/sc_obstack.c _obstack_begin_1
+    src/sc_obstack.c:212:35: warning: Dereference of null pointer.
+      h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
+                                      ^~~~~~~~~~~
+    ANALYZE: src/sc_obstack.c _obstack_newchunk
+    src/sc_obstack.c:250:3: warning: Dereference of null pointer.
+      new_chunk->prev = old_chunk;
+      ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+  
+  [0] http://clang.llvm.org/StaticAnalysis.html
+type: :bugfix
+component: libsc
+release: 
+reporter: Lucas C Wilcox <lucasw at ices.utexas.edu>
+status: :closed
+disposition: :wontfix
+creation_time: 2008-10-17 17:06:11.024651 Z
+references: []
+
+id: d1c07123f50402055f9cc973e68872d7ed05fc7b
+log_events: 
+- - 2008-10-17 17:06:15.640323 Z
+  - Lucas C Wilcox <lucasw at ices.utexas.edu>
+  - created
+  - Initial report
+- - 2008-10-29 17:53:50.709546 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition wontfix
+  - |-
+    The warnings for sc_options.c have been addressed.
+    The remaining ones are for sc_getopt and sc_obstack which is glibc code.
diff --git a/sc/bugs/issue-f06e2b2e596ab478be598cfcf380d688eb1fa8b6.yaml b/sc/bugs/issue-f06e2b2e596ab478be598cfcf380d688eb1fa8b6.yaml
new file mode 100644
index 0000000..9d80d48
--- /dev/null
+++ b/sc/bugs/issue-f06e2b2e596ab478be598cfcf380d688eb1fa8b6.yaml
@@ -0,0 +1,34 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Builtin getopt segfaults on the mac
+desc: |-
+  The getopt in libsc fails on the mac.
+  
+  For example :
+    ./example/options/sc_options -d 2
+    [libsc] Preload not found or failed
+    [1]    69474 bus error  ./example/options/sc_options -d 2
+  
+  The backtrace I get from gdb is:
+  
+  The workaround is to pass --without-getopt to configure.
+type: :bugfix
+component: libsc
+release: "1.6"
+reporter: Lucas C Wilcox <lucasw at ices.utexas.edu>
+status: :closed
+disposition: :wontfix
+creation_time: 2008-10-17 16:58:25.336995 Z
+references: []
+
+id: f06e2b2e596ab478be598cfcf380d688eb1fa8b6
+log_events: 
+- - 2008-10-17 16:58:37.529171 Z
+  - Lucas C Wilcox <lucasw at ices.utexas.edu>
+  - created
+  - Initial report
+- - 2008-10-28 21:46:56.521206 Z
+  - Lucas C Wilcox <lucasw at ices.utexas.edu>
+  - closed with disposition wontfix
+  - |-
+    The segfault was curcumvented by not including sc's getopt on
+    systems that have getopt_long.
diff --git a/sc/bugs/issue-f6e1a861093ae06fe8ea3826f4fbb3c4247dc2a8.yaml b/sc/bugs/issue-f6e1a861093ae06fe8ea3826f4fbb3c4247dc2a8.yaml
new file mode 100644
index 0000000..d3374ad
--- /dev/null
+++ b/sc/bugs/issue-f6e1a861093ae06fe8ea3826f4fbb3c4247dc2a8.yaml
@@ -0,0 +1,29 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Check C99 conformance
+desc: |-
+  Can the files sc_c99_functions be removed? Or changed?
+  What is the right way to handle C99 functions and prototypes?
+  How to deal with feature test macros for glibc?
+type: :bugfix
+component: libsc
+release: 
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :unstarted
+disposition: 
+creation_time: 2009-07-21 21:12:21.823496 Z
+references: []
+
+id: f6e1a861093ae06fe8ea3826f4fbb3c4247dc2a8
+log_events: 
+- - 2009-07-21 21:12:23.783247 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-07-21 21:14:24.387055 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - assigned to release 1.6 from unassigned
+  - ""
+- - 2010-01-22 17:33:37.936420 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - unassigned from release 1.6
+  - Also remove tgmath.h from sc_complex.h for portability.
diff --git a/sc/bugs/issue-fbfa7595e16582a783ea86bbedd1ae38049faecf.yaml b/sc/bugs/issue-fbfa7595e16582a783ea86bbedd1ae38049faecf.yaml
new file mode 100644
index 0000000..381b5d2
--- /dev/null
+++ b/sc/bugs/issue-fbfa7595e16582a783ea86bbedd1ae38049faecf.yaml
@@ -0,0 +1,22 @@
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Remove printf in favor of logging functions
+desc: There's a few occurences of printf left that print to stdout/stderr.
+type: :bugfix
+component: libsc
+release: "1.6"
+reporter: Carsten Burstedde <carsten at ices.utexas.edu>
+status: :closed
+disposition: :fixed
+creation_time: 2009-10-07 20:32:07.149432 Z
+references: []
+
+id: fbfa7595e16582a783ea86bbedd1ae38049faecf
+log_events: 
+- - 2009-10-07 20:32:07.693302 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - created
+  - ""
+- - 2009-11-19 18:57:34.512330 Z
+  - Carsten Burstedde <carsten at ices.utexas.edu>
+  - closed with disposition fixed
+  - The abort functions now use standard logging with SC_LP_ERROR.
diff --git a/sc/bugs/project.yaml b/sc/bugs/project.yaml
new file mode 100644
index 0000000..af179cd
--- /dev/null
+++ b/sc/bugs/project.yaml
@@ -0,0 +1,29 @@
+--- !ditz.rubyforge.org,2008-03-06/project 
+name: libsc
+version: "1.1"
+components: 
+- !ditz.rubyforge.org,2008-03-06/component 
+  name: libsc
+releases: 
+- !ditz.rubyforge.org,2008-03-06/release 
+  name: "1.5"
+  status: :released
+  release_time: 2008-10-17 16:34:16.044402 Z
+  log_events: 
+  - - 2008-10-17 16:33:46.621384 Z
+    - Carsten Burstedde <carsten at ices.utexas.edu>
+    - created
+    - This release is included with the p4est-0.2 branch in Deal.II.
+  - - 2008-10-17 16:34:16.044418 Z
+    - Carsten Burstedde <carsten at ices.utexas.edu>
+    - released
+    - Current maintenance revision of this release is 0.5.4.
+- !ditz.rubyforge.org,2008-03-06/release 
+  name: "1.6"
+  status: :unreleased
+  release_time: 
+  log_events: 
+  - - 2008-10-17 16:34:49.627202 Z
+    - Carsten Burstedde <carsten at ices.utexas.edu>
+    - created
+    - This release is developed alongside p4est-0.3.
diff --git a/sc/build-aux/git-version-gen b/sc/build-aux/git-version-gen
new file mode 100755
index 0000000..b6c9d41
--- /dev/null
+++ b/sc/build-aux/git-version-gen
@@ -0,0 +1,162 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2008-04-08.07
+
+# Copyright (C) 2007-2008 Free Software Foundation
+#
+# 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, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+#   produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+#   presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+#   a checked-out repository.  Created with contents that were learned at
+#   the last time autoconf was run, and used by git-version-gen.  Must not
+#   be present in either $(srcdir) or $(builddir) for git-version-gen to
+#   give accurate answers during normal development with a checked out tree,
+#   but must be present in a tarball when there is no version control system.
+#   Therefore, it cannot be used in any dependencies.  GNUmakefile has
+#   hooks to force a reconfigure at distribution time to get the value
+#   correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+#   tarball.  Usable in dependencies, particularly for files that don't
+#   want to depend on config.h but do want to track version changes.
+#   Delete this file prior to any autoconf run where you want to rebuild
+#   files to pick up a version string change; and leave it stale to
+#   minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+#         m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+#         [bug-project at example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+#	echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+#	echo $(VERSION) > $(distdir)/.tarball-version
+
+# Copyright (C) 2014 Carsten Burstedde
+#
+# With the new style of git submodules, .git may be a file: added test -f .git
+
+case $# in
+  1) ;;
+  *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
+esac
+
+srcdir=`dirname $1`
+tarball_version_file=`basename $1`
+nl='
+'
+
+# Change directory into the srcdir.  This should allow
+# for out of source make dist.
+cd $srcdir
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+  v=`cat $tarball_version_file` || exit 1
+  case $v in
+    *$nl*) v= ;; # reject multi-line output
+    [0-9]*) ;;
+    *) v= ;;
+  esac
+  test -z "$v" \
+  && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+
+if test -n "$v"
+then
+  : # use $v
+elif { test -d .git || test -f .git ; } \
+  && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
+  || git describe --abbrev=4 HEAD 2>/dev/null` \
+  && case $v in
+  v[0-9]*) ;;
+  *) (exit 1) ;;
+esac
+then
+  # Is this a new git that lists number of commits since the last
+  # tag or the previous older version that did not?
+  #   Newer: v6.10-77-g0f8faeb
+  #   Older: v6.10-g0f8faeb
+  case $v in
+    *-*-*) : git describe is okay three part flavor ;;
+    *-*)
+    : git describe is older two part flavor
+    # Recreate the number of commits and rewrite such that the
+    # result is the same as if we were using the newer version
+    # of git describe.
+    vtag=`echo "$v" | sed 's/-.*//'`
+    numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+    v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+    ;;
+  esac
+
+    # Change the first '-' to a '.', so version-comparing tools work properly.
+    # Remove the "g" in git describe's output string, to save a byte.
+    v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+else
+    v=UNKNOWN
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git status > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+  '') ;;
+  *) # Append the suffix only if there isn't one already.
+  case $v in
+    *-dirty) ;;
+    *) v="$v-dirty" ;;
+  esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d '\012'
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/sc/build-aux/git2cl b/sc/build-aux/git2cl
new file mode 100755
index 0000000..aa1e8c1
--- /dev/null
+++ b/sc/build-aux/git2cl
@@ -0,0 +1,308 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2007 Simon Josefsson.
+#
+# The functions mywrap, last_line_len, wrap_log_entry are derived from
+# the cvs2cl tool, see <http://www.red-bean.com/cvs2cl/>:
+# Copyright (C) 2001,2002,2003,2004 Martyn J. Pearce <fluffy at cpan.org>
+# Copyright (C) 1999 Karl Fogel <kfogel at red-bean.com>
+#
+# git2cl is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# git2cl 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 git2cl; see the file COPYING.  If not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+use strict;
+use Date::Parse qw(strptime);
+use POSIX qw(strftime);
+use Text::Wrap qw(wrap);
+
+use constant EMPTY_LOG_MESSAGE => '*** empty log message ***';
+
+sub mywrap {
+    my ($indent1, $indent2, @text) = @_;
+    # If incoming text looks preformatted, don't get clever
+    my $text = Text::Wrap::wrap($indent1, $indent2, @text);
+    if ( grep /^\s+/m, @text ) {
+	return $text;
+    }
+    my @lines = split /\n/, $text;
+    $indent2 =~ s!^((?: {8})+)!"\t" x (length($1)/8)!e;
+    $lines[0] =~ s/^$indent1\s+/$indent1/;
+    s/^$indent2\s+/$indent2/
+	for @lines[1..$#lines];
+    my $newtext = join "\n", @lines;
+    $newtext .= "\n"
+	if substr($text, -1) eq "\n";
+    return $newtext;
+}
+
+sub last_line_len {
+    my $files_list = shift;
+    my @lines = split (/\n/, $files_list);
+    my $last_line = pop (@lines);
+    return length ($last_line);
+}
+
+# A custom wrap function, sensitive to some common constructs used in
+# log entries.
+sub wrap_log_entry {
+    my $text = shift;                  # The text to wrap.
+    my $left_pad_str = shift;          # String to pad with on the left.
+
+    # These do NOT take left_pad_str into account:
+    my $length_remaining = shift;      # Amount left on current line.
+    my $max_line_length  = shift;      # Amount left for a blank line.
+
+    my $wrapped_text = '';             # The accumulating wrapped entry.
+    my $user_indent = '';              # Inherited user_indent from prev line.
+
+    my $first_time = 1;                # First iteration of the loop?
+    my $suppress_line_start_match = 0; # Set to disable line start checks.
+
+    my @lines = split (/\n/, $text);
+    while (@lines)   # Don't use `foreach' here, it won't work.
+    {
+	my $this_line = shift (@lines);
+	chomp $this_line;
+
+	if ($this_line =~ /^(\s+)/) {
+	    $user_indent = $1;
+	}
+	else {
+	    $user_indent = '';
+	}
+
+	# If it matches any of the line-start regexps, print a newline now...
+	if ($suppress_line_start_match)
+	{
+	    $suppress_line_start_match = 0;
+	}
+	elsif (($this_line =~ /^(\s*)\*\s+[a-zA-Z0-9]/)
+	       || ($this_line =~ /^(\s*)\* [a-zA-Z0-9_\.\/\+-]+/)
+	       || ($this_line =~ /^(\s*)\([a-zA-Z0-9_\.\/\+-]+(\)|,\s*)/)
+	       || ($this_line =~ /^(\s+)(\S+)/)
+	       || ($this_line =~ /^(\s*)- +/)
+	       || ($this_line =~ /^()\s*$/)
+	       || ($this_line =~ /^(\s*)\*\) +/)
+	       || ($this_line =~ /^(\s*)[a-zA-Z0-9](\)|\.|\:) +/))
+	{
+	    $length_remaining = $max_line_length - (length ($user_indent));
+	}
+
+	# Now that any user_indent has been preserved, strip off leading
+	# whitespace, so up-folding has no ugly side-effects.
+	$this_line =~ s/^\s*//;
+
+	# Accumulate the line, and adjust parameters for next line.
+	my $this_len = length ($this_line);
+	if ($this_len == 0)
+	{
+	    # Blank lines should cancel any user_indent level.
+	    $user_indent = '';
+	    $length_remaining = $max_line_length;
+	}
+	elsif ($this_len >= $length_remaining) # Line too long, try breaking it.
+	{
+	    # Walk backwards from the end.  At first acceptable spot, break
+	    # a new line.
+	    my $idx = $length_remaining - 1;
+	    if ($idx < 0) { $idx = 0 };
+	    while ($idx > 0)
+	    {
+		if (substr ($this_line, $idx, 1) =~ /\s/)
+		{
+		    my $line_now = substr ($this_line, 0, $idx);
+		    my $next_line = substr ($this_line, $idx);
+		    $this_line = $line_now;
+
+		    # Clean whitespace off the end.
+		    chomp $this_line;
+
+		    # The current line is ready to be printed.
+		    $this_line .= "\n${left_pad_str}";
+
+		    # Make sure the next line is allowed full room.
+		    $length_remaining = $max_line_length - (length ($user_indent));
+
+		    # Strip next_line, but then preserve any user_indent.
+		    $next_line =~ s/^\s*//;
+
+		    # Sneak a peek at the user_indent of the upcoming line, so
+		    # $next_line (which will now precede it) can inherit that
+		    # indent level.  Otherwise, use whatever user_indent level
+		    # we currently have, which might be none.
+		    my $next_next_line = shift (@lines);
+		    if ((defined ($next_next_line)) && ($next_next_line =~ /^(\s+)/)) {
+			$next_line = $1 . $next_line if (defined ($1));
+			# $length_remaining = $max_line_length - (length ($1));
+			$next_next_line =~ s/^\s*//;
+		    }
+		    else {
+			$next_line = $user_indent . $next_line;
+		    }
+		    if (defined ($next_next_line)) {
+			unshift (@lines, $next_next_line);
+		    }
+		    unshift (@lines, $next_line);
+
+		    # Our new next line might, coincidentally, begin with one of
+		    # the line-start regexps, so we temporarily turn off
+		    # sensitivity to that until we're past the line.
+		    $suppress_line_start_match = 1;
+
+		    last;
+		}
+		else
+		{
+		    $idx--;
+		}
+	    }
+
+	    if ($idx == 0)
+	    {
+		# We bottomed out because the line is longer than the
+		# available space.  But that could be because the space is
+		# small, or because the line is longer than even the maximum
+		# possible space.  Handle both cases below.
+
+		if ($length_remaining == ($max_line_length - (length ($user_indent))))
+		{
+		    # The line is simply too long -- there is no hope of ever
+		    # breaking it nicely, so just insert it verbatim, with
+		    # appropriate padding.
+		    $this_line = "\n${left_pad_str}${this_line}";
+		}
+		else
+		{
+		    # Can't break it here, but may be able to on the next round...
+		    unshift (@lines, $this_line);
+		    $length_remaining = $max_line_length - (length ($user_indent));
+		    $this_line = "\n${left_pad_str}";
+		}
+	    }
+	}
+	else  # $this_len < $length_remaining, so tack on what we can.
+	{
+	    # Leave a note for the next iteration.
+	    $length_remaining = $length_remaining - $this_len;
+
+	    if ($this_line =~ /\.$/)
+	    {
+		$this_line .= "  ";
+		$length_remaining -= 2;
+	    }
+	    else  # not a sentence end
+	    {
+		$this_line .= " ";
+		$length_remaining -= 1;
+	    }
+	}
+
+	# Unconditionally indicate that loop has run at least once.
+	$first_time = 0;
+
+	$wrapped_text .= "${user_indent}${this_line}";
+    }
+
+    # One last bit of padding.
+    $wrapped_text .= "\n";
+
+    return $wrapped_text;
+}
+
+# main
+
+my @date;
+my $author;
+my @files;
+my $comment;
+my $merge;
+
+my $state; # 0-header 1-comment 2-files
+my $done = 0;
+
+$state = 0;
+
+while (<>) {
+    #print STDERR "debug ($state, " . (@date ? (strftime "%Y-%m-%d", @date) : "") . "): `$_'\n";
+
+    if ($state == 0) {
+	if (m,^Author: (.*),) {
+	    $author = $1;
+	}
+	if (m,^Date: (.*),) {
+	    @date = strptime($1);
+	}
+	if (m,^Merge: (.*),) {
+	    $merge = 1;
+	}
+	$state = 1 if (m,^$,);
+    } elsif ($state == 1) {
+	$state = 2 if (m,^$,);
+	s/^    //g;
+	s/\n/ /g;
+	$comment = $comment . $_;
+    } elsif ($state == 2 && $merge) {
+	$done = 1;
+    } elsif ($state == 2) {
+	if (m,^([-0-9]+)\t([-0-9]+)\t(.*)$,) {
+	    push @files, $3;
+	} elsif (m,^[^ ],) {
+	    # No file changes.
+	    $done = 1;
+	}
+	$done = 1 if (m,^$,);
+    }
+
+    if ($done && @date == ()) {
+	print STDERR "warning: could not parse entry\n";
+    } elsif ($done) {
+	print (strftime "%Y-%m-%d  $author\n\n", @date);
+
+	my $files = join (", ", @files);
+	$files = mywrap ("\t", "\t", "* $files"), ": ";
+
+	if (index($comment, EMPTY_LOG_MESSAGE) > -1 ) {
+	    $comment = "[no log message]\n";
+	}
+
+	my $files_last_line_len = 0;
+	$files_last_line_len = last_line_len($files) + 1;
+	my $msg = wrap_log_entry($comment, "\t", 69-$files_last_line_len, 69);
+
+	$msg =~ s/[ \t]+\n/\n/g;
+
+	if ($merge) {
+	    print "\t$msg\n";
+	} else {
+	    print "$files: $msg\n";
+	}
+
+	@date = ();
+	$author = "";
+	@files = ();
+	$comment = "";
+	$merge = 0;
+
+	$state = 0;
+	$done = 0;
+    }
+}
+
+if (@files) {
+    print (strftime "%Y-%m-%d  $author\n\n", @date);
+    my $msg = wrap_log_entry($comment, "\t", 69, 69);
+    $msg =~ s/[ \t]+\n/\n/g;
+    print "\t* $msg\n";
+}
diff --git a/sc/config/ax_prefix_config_h.m4 b/sc/config/ax_prefix_config_h.m4
new file mode 100644
index 0000000..c17563f
--- /dev/null
+++ b/sc/config/ax_prefix_config_h.m4
@@ -0,0 +1,203 @@
+# ===========================================================================
+#    http://www.gnu.org/software/autoconf-archive/ax_prefix_config_h.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PREFIX_CONFIG_H [(OUTPUT-HEADER [,PREFIX [,ORIG-HEADER]])]
+#
+# DESCRIPTION
+#
+#   Generate an installable config.h.
+#
+#   A package should not normally install its config.h as a system header,
+#   but if it must, this macro can be used to avoid namespace pollution by
+#   making a copy of config.h with a prefix added to all the macro names.
+#
+#   Each "#define SOMEDEF" line of the configuration header has the given
+#   prefix added, in the same case as the first character of the macro name.
+#
+#   Defaults:
+#
+#     OUTPUT-HEADER = $PACKAGE-config.h
+#     PREFIX = $PACKAGE
+#     ORIG-HEADER, from AM_CONFIG_HEADER(config.h)
+#
+#   Your configure.ac script should contain both macros in this order.
+#
+#   Example:
+#
+#     AC_INIT(config.h.in)        # config.h.in as created by "autoheader"
+#     AM_INIT_AUTOMAKE(testpkg, 0.1.1)    # makes #undef VERSION and PACKAGE
+#     AM_CONFIG_HEADER(config.h)          # prep config.h from config.h.in
+#     AX_PREFIX_CONFIG_H(mylib/_config.h) # prep mylib/_config.h from it..
+#     AC_MEMORY_H                         # makes "#undef NEED_MEMORY_H"
+#     AC_C_CONST_H                        # makes "#undef const"
+#     AC_OUTPUT(Makefile)                 # creates the "config.h" now
+#                                         # and also mylib/_config.h
+#
+#   If the argument to AX_PREFIX_CONFIG_H would have been omitted then the
+#   default output file would have been called simply "testpkg-config.h",
+#   but even under the name "mylib/_config.h" it contains prefix-defines
+#   like
+#
+#     #ifndef TESTPKG_VERSION
+#     #define TESTPKG_VERSION "0.1.1"
+#     #endif
+#     #ifndef TESTPKG_NEED_MEMORY_H
+#     #define TESTPKG_NEED_MEMORY_H 1
+#     #endif
+#     #ifndef _testpkg_const
+#     #define _testpkg_const _const
+#     #endif
+#
+#   and this "mylib/_config.h" can be installed along with other header
+#   files, which is most convenient when creating a shared library (that has
+#   some headers) whose functionality depends on features detected at
+#   compile-time. No need to invent some "mylib-confdefs.h.in" manually.
+#
+#   Note that some AC_DEFINEs that end up in the config.h file are actually
+#   self-referential - e.g. AC_C_INLINE, AC_C_CONST, and the AC_TYPE_OFF_T
+#   say that they "will define inline|const|off_t if the system does not do
+#   it by itself". You might want to clean up about these - consider an
+#   extra mylib/conf.h that reads something like:
+#
+#     #include <mylib/_config.h>
+#     #ifndef _testpkg_const
+#     #define _testpkg_const const
+#     #endif
+#
+#   and then start using _testpkg_const in the header files. That is also a
+#   good thing to differentiate whether some library-user has starting to
+#   take up with a different compiler, so perhaps it could read something
+#   like this:
+#
+#     #ifdef _MSC_VER
+#     #include <mylib/_msvc.h>
+#     #else
+#     #include <mylib/_config.h>
+#     #endif
+#     #ifndef _testpkg_const
+#     #define _testpkg_const const
+#     #endif
+#
+# LICENSE
+#
+#   Copyright (c) 2014 Reuben Thomas <rrt at sc3d.org>
+#   Copyright (c) 2008 Guido U. Draheim <guidod at gmx.de>
+#   Copyright (c) 2008 Marten Svantesson
+#   Copyright (c) 2008 Gerald Point <Gerald.Point at labri.fr>
+#
+#   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/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 15
+
+AC_DEFUN([AX_PREFIX_CONFIG_H],[dnl
+AC_PREREQ([2.62])
+AC_BEFORE([AC_CONFIG_HEADERS],[$0])dnl
+AC_CONFIG_COMMANDS(m4_default([$1], [$PACKAGE-config.h]),[dnl
+AS_VAR_PUSHDEF([_OUT],[ac_prefix_conf_OUT])dnl
+AS_VAR_PUSHDEF([_DEF],[ac_prefix_conf_DEF])dnl
+AS_VAR_PUSHDEF([_PKG],[ac_prefix_conf_PKG])dnl
+AS_VAR_PUSHDEF([_LOW],[ac_prefix_conf_LOW])dnl
+AS_VAR_PUSHDEF([_UPP],[ac_prefix_conf_UPP])dnl
+AS_VAR_PUSHDEF([_INP],[ac_prefix_conf_INP])dnl
+m4_pushdef([_script],[conftest.prefix])dnl
+m4_pushdef([_symbol],[m4_cr_Letters[]m4_cr_digits[]_])dnl
+_OUT=`echo m4_default([$1], [$PACKAGE-config.h])`
+_DEF=`echo _$_OUT | sed -e "y:m4_cr_letters:m4_cr_LETTERS[]:" -e "s/@<:@^m4_cr_Letters@:>@/_/g"`
+_PKG=`echo m4_default([$2], [$PACKAGE])`
+_LOW=`echo _$_PKG | sed -e "y:m4_cr_LETTERS-:m4_cr_letters[]_:"`
+_UPP=`echo $_PKG | sed -e "y:m4_cr_letters-:m4_cr_LETTERS[]_:"  -e "/^@<:@m4_cr_digits@:>@/s/^/_/"`
+_INP=`echo "$3" | sed -e 's/ *//'`
+if test ".$_INP" = "."; then
+   for ac_file in : $CONFIG_HEADERS; do test "_$ac_file" = _: && continue
+     case "$ac_file" in
+        *.h) _INP=$ac_file ;;
+        *)
+     esac
+     test ".$_INP" != "." && break
+   done
+fi
+if test ".$_INP" = "."; then
+   case "$_OUT" in
+      */*) _INP=`basename "$_OUT"`
+      ;;
+      *-*) _INP=`echo "$_OUT" | sed -e "s/@<:@_symbol@:>@*-//"`
+      ;;
+      *) _INP=config.h
+      ;;
+   esac
+fi
+if test -z "$_PKG" ; then
+   AC_MSG_ERROR([no prefix for _PREFIX_PKG_CONFIG_H])
+else
+  if test ! -f "$_INP" ; then if test -f "$srcdir/$_INP" ; then
+     _INP="$srcdir/$_INP"
+  fi fi
+  AC_MSG_NOTICE(creating $_OUT - prefix $_UPP for $_INP defines)
+  if test -f $_INP ; then
+    AS_ECHO(["s/^@%:@undef  *\\(@<:@m4_cr_LETTERS[]_@:>@\\)/@%:@undef $_UPP""_\\1/"]) > _script
+    AS_ECHO(["s/^@%:@undef  *\\(@<:@m4_cr_letters@:>@\\)/@%:@undef $_LOW""_\\1/"]) >> _script
+    AS_ECHO(["s/^@%:@def[]ine  *\\(@<:@m4_cr_LETTERS[]_@:>@@<:@_symbol@:>@*\\)\\(.*\\)/@%:@ifndef $_UPP""_\\1\\"]) >> _script
+    AS_ECHO(["@%:@def[]ine $_UPP""_\\1\\2\\"]) >> _script
+    AS_ECHO(["@%:@endif/"]) >> _script
+    AS_ECHO(["s/^@%:@def[]ine  *\\(@<:@m4_cr_letters@:>@@<:@_symbol@:>@*\\)\\(.*\\)/@%:@ifndef $_LOW""_\\1\\"]) >> _script
+    AS_ECHO(["@%:@define $_LOW""_\\1\\2\\"]) >> _script
+    AS_ECHO(["@%:@endif/"]) >> _script
+    # now executing _script on _DEF input to create _OUT output file
+    echo "@%:@ifndef $_DEF"      >$tmp/pconfig.h
+    echo "@%:@def[]ine $_DEF 1" >>$tmp/pconfig.h
+    echo ' ' >>$tmp/pconfig.h
+    echo /'*' $_OUT. Generated automatically at end of configure. '*'/ >>$tmp/pconfig.h
+
+    sed -f _script $_INP >>$tmp/pconfig.h
+    echo ' ' >>$tmp/pconfig.h
+    echo '/* once:' $_DEF '*/' >>$tmp/pconfig.h
+    echo "@%:@endif" >>$tmp/pconfig.h
+    if cmp -s $_OUT $tmp/pconfig.h 2>/dev/null; then
+      AC_MSG_NOTICE([$_OUT is unchanged])
+    else
+      ac_dir=`AS_DIRNAME(["$_OUT"])`
+      AS_MKDIR_P(["$ac_dir"])
+      rm -f "$_OUT"
+      mv $tmp/pconfig.h "$_OUT"
+    fi
+  else
+    AC_MSG_ERROR([input file $_INP does not exist - skip generating $_OUT])
+  fi
+  rm -f conftest.*
+fi
+m4_popdef([_symbol])dnl
+m4_popdef([_script])dnl
+AS_VAR_POPDEF([_INP])dnl
+AS_VAR_POPDEF([_UPP])dnl
+AS_VAR_POPDEF([_LOW])dnl
+AS_VAR_POPDEF([_PKG])dnl
+AS_VAR_POPDEF([_DEF])dnl
+AS_VAR_POPDEF([_OUT])dnl
+],[PACKAGE="$PACKAGE"])])
diff --git a/sc/config/ax_split_version.m4 b/sc/config/ax_split_version.m4
new file mode 100644
index 0000000..02737c3
--- /dev/null
+++ b/sc/config/ax_split_version.m4
@@ -0,0 +1,38 @@
+# ===========================================================================
+#     http://www.gnu.org/software/autoconf-archive/ax_split_version.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_SPLIT_VERSION
+#
+# DESCRIPTION
+#
+#   Splits a version number in the format MAJOR.MINOR.POINT into its
+#   separate components.
+#
+#   Sets the variables.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Tom Howard <tomhoward at users.sf.net>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 9
+
+AC_DEFUN([AX_SPLIT_VERSION],[
+    AC_REQUIRE([AC_PROG_SED])
+    AX_MAJOR_VERSION=`echo "$VERSION" | $SED 's/\([[^.]][[^.]]*\).*/\1/'`
+    AX_MINOR_VERSION=`echo "$VERSION" | $SED 's/[[^.]][[^.]]*.\([[^.]][[^.]]*\).*/\1/'`
+    AX_POINT_VERSION=`echo "$VERSION" | $SED 's/[[^.]][[^.]]*.[[^.]][[^.]]*.\(.*\)/\1/'`
+    AC_MSG_CHECKING([Major version])
+    AC_MSG_RESULT([$AX_MAJOR_VERSION])
+    AC_MSG_CHECKING([Minor version])
+    AC_MSG_RESULT([$AX_MINOR_VERSION])
+    AC_MSG_CHECKING([Point version])
+    AC_MSG_RESULT([$AX_POINT_VERSION])
+])
diff --git a/sc/config/sc_blas.m4 b/sc/config/sc_blas.m4
new file mode 100644
index 0000000..aa89b3d
--- /dev/null
+++ b/sc/config/sc_blas.m4
@@ -0,0 +1,274 @@
+dnl This is a modified version of the Teuchos config dir from Trilinos
+dnl with the following license.
+dnl
+dnl ***********************************************************************
+dnl
+dnl                    Teuchos: Common Tools Package
+dnl                 Copyright (2004) Sandia Corporation
+dnl
+dnl Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
+dnl license for use of this work by or on behalf of the U.S. Government.
+dnl
+dnl This library is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU Lesser General Public License as
+dnl published by the Free Software Foundation; either version 2.1 of the
+dnl License, or (at your option) any later version.
+dnl
+dnl This library is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+dnl Lesser General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU Lesser General Public
+dnl License along with this library; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+dnl USA
+dnl Questions? Contact Michael A. Heroux (maherou at sandia.gov)
+dnl
+dnl ***********************************************************************
+dnl
+dnl @synopsis SC_BLAS(PREFIX, DGEMM-FUNCTION,
+dnl                   [ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+dnl
+dnl This macro looks for a library that implements the BLAS
+dnl linear-algebra interface (see http://www.netlib.org/blas/).
+dnl On success, it sets the BLAS_LIBS output variable to
+dnl hold the requisite library linkages.
+dnl
+dnl To link with BLAS, you should link with:
+dnl
+dnl 	$BLAS_LIBS $LIBS $FLIBS
+dnl
+dnl in that order.  FLIBS is the output variable of the
+dnl AC_F77_LIBRARY_LDFLAGS macro (called if necessary by SC_BLAS),
+dnl and is sometimes necessary in order to link with F77 libraries.
+dnl Users will also need to use AC_F77_DUMMY_MAIN (see the autoconf
+dnl manual), for the same reason.
+dnl
+dnl Many libraries are searched for, from ATLAS to CXML to ESSL.
+dnl The user may also use --with-blas=<lib> in order to use some
+dnl specific BLAS library <lib>.  In order to link successfully,
+dnl however, be aware that you will probably need to use the same
+dnl Fortran compiler (which can be set via the F77 env. var.) as
+dnl was used to compile the BLAS library.
+dnl
+dnl ACTION-IF-FOUND is a list of shell commands to run if a BLAS
+dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands
+dnl to run it if it is not found.  If ACTION-IF-FOUND is not specified,
+dnl the default action will define HAVE_BLAS.
+dnl
+dnl This macro requires autoconf 2.50 or later.
+dnl
+dnl @version $Id: acx_blas.m4,v 1.3 2006/04/21 02:29:27 jmwille Exp $
+dnl @author Steven G. Johnson <stevenj at alum.mit.edu>
+dnl
+dnl Edited by Jim Willenbring on 5-14-2004 to check for dgemm instead of
+dnl sgemm.
+dnl Edited by Jim Willenbring on 4-17-2006 to stop looking for BLAS if
+dnl a specific BLAS library specified by a user cannot be used.
+
+dnl Edited by Carsten Burstedde <carsten at ices.utexas.edu>
+dnl Expect the F77_ autoconf macros to be called outside of this file.
+dnl Take as argument a mangled DGEMM function to check for.
+dnl This way the SC_BLAS macro can be called multiple times
+dnl with different Fortran environments to minimize F77 dependencies.
+dnl Replaced obsolete AC_TRY_LINK_FUNC macro.
+dnl Disabled the PhiPack test since it requires BLAS_LIBS anyway.
+dnl Fixed buggy generic Mac OS X library test.
+
+dnl Subroutine to link a program using blas
+dnl SC_BLAS_LINK (<added to CHECKING message>)
+AC_DEFUN([SC_BLAS_LINK], [
+        AC_MSG_CHECKING([for BLAS by linking a C program$1])
+        AC_LINK_IFELSE([AC_LANG_PROGRAM(dnl
+[[
+#ifdef __cplusplus
+extern "C"
+void $sc_blas_func (char *, char *, int *, int *, int *, double *, double *,
+                    int *, double *, int *, double *, double *, int *);
+#endif
+]], [[
+int     i = 1;
+double  alpha = 1., beta = 1.;
+double  A = 1., B = 1., C = 1.;
+$sc_blas_func ("N", "N", &i, &i, &i, &alpha, &A, &i, &B, &i, &beta, &C, &i);
+]])],
+[AC_MSG_RESULT([successful])],
+[AC_MSG_RESULT([failed]); sc_blas_ok=no])
+])
+
+dnl The first argument of this macro should be the package prefix.
+dnl The second argument of this macro should be a mangled DGEMM function.
+AC_DEFUN([SC_BLAS], [
+AC_PREREQ(2.50)
+dnl Expect this to be called already.
+dnl AC_REQUIRE([AC_F77_LIBRARY_LDFLAGS])
+dnl AC_REQUIRE([AC_F77_WRAPPERS])
+sc_blas_ok=no
+user_spec_blas_failed=no
+
+AC_ARG_WITH([blas], [AS_HELP_STRING([--with-blas=<lib>],
+            [change default BLAS library to <lib>
+             or specify --without-blas to use no BLAS and LAPACK at all])],,
+	     [withval=yes])
+case $withval in
+	yes | "") ;;
+	no) sc_blas_ok=disable ;;
+	-* | */* | *.a | *.so | *.so.* | *.o) BLAS_LIBS="$withval" ;;
+	*) BLAS_LIBS="-l$withval" ;;
+esac
+
+dnl Expect the mangled DGEMM function name to be in $2.
+sc_blas_func="$2"
+
+sc_blas_save_LIBS="$LIBS"
+LIBS="$LIBS $FLIBS"
+
+# First, check BLAS_LIBS environment variable
+if test "x$sc_blas_ok" = xno; then
+if test "x$BLAS_LIBS" != x; then
+	save_LIBS="$LIBS"; LIBS="$BLAS_LIBS $LIBS"
+	AC_MSG_CHECKING([for $sc_blas_func in $BLAS_LIBS])
+	AC_LINK_IFELSE([AC_LANG_CALL([], [$sc_blas_func])],
+                       [sc_blas_ok=yes], [user_spec_blas_failed=yes])
+	AC_MSG_RESULT($sc_blas_ok)
+	LIBS="$save_LIBS"
+fi
+fi
+
+# If the user specified a blas library that could not be used we will
+# halt the search process rather than risk finding a blas library that
+# the user did not specify.
+
+if test "x$user_spec_blas_failed" != xyes; then
+
+# BLAS linked to by default?  (happens on some supercomputers)
+if test "x$sc_blas_ok" = xno; then
+	AC_CHECK_FUNC($sc_blas_func, [sc_blas_ok=yes])
+fi
+
+# BLAS in ATLAS library? (http://math-atlas.sourceforge.net/)
+if test "x$sc_blas_ok" = xno; then
+	AC_CHECK_LIB(atlas, ATL_xerbla,
+		[AC_CHECK_LIB(f77blas, $sc_blas_func,
+		[AC_CHECK_LIB(cblas, cblas_dgemm,
+			[sc_blas_ok=yes
+			 BLAS_LIBS="-lcblas -lf77blas -latlas"],
+			[], [-lf77blas -latlas])],
+			[], [-latlas])])
+fi
+
+# BLAS in PhiPACK libraries? (requires generic BLAS lib, too)
+# Disabled since we might want more than sgemm and dgemm.
+if test "x$sc_blas_ok" = xno && false ; then
+	AC_CHECK_LIB(blas, $dgemm,
+		[AC_CHECK_LIB(dgemm, $dgemm,
+		[AC_CHECK_LIB(sgemm, $sgemm,
+			[sc_blas_ok=yes; BLAS_LIBS="-lsgemm -ldgemm -lblas"],
+			[], [-lblas])],
+			[], [-lblas])])
+fi
+
+# BLAS in Intel MKL library?
+if test "x$sc_blas_ok" = xno; then
+	AC_CHECK_LIB(mkl, $sc_blas_func, [sc_blas_ok=yes;BLAS_LIBS="-lmkl"])
+fi
+
+# BLAS in Apple vecLib library?
+if test "x$sc_blas_ok" = xno; then
+	save_LIBS="$LIBS"; LIBS="-framework vecLib $LIBS"
+	AC_CHECK_FUNC($sc_blas_func, [sc_blas_ok=yes;BLAS_LIBS="-framework vecLib"])
+	LIBS="$save_LIBS"
+fi
+
+# BLAS in Alpha CXML library?
+if test "x$sc_blas_ok" = xno; then
+	AC_CHECK_LIB(cxml, $sc_blas_func, [sc_blas_ok=yes;BLAS_LIBS="-lcxml"])
+fi
+
+# BLAS in Alpha DXML library? (now called CXML, see above)
+if test "x$sc_blas_ok" = xno; then
+	AC_CHECK_LIB(dxml, $sc_blas_func, [sc_blas_ok=yes;BLAS_LIBS="-ldxml"])
+fi
+
+# BLAS in Sun Performance library?
+if test "x$sc_blas_ok" = xno; then
+	if test "x$GCC" != xyes; then # only works with Sun CC
+		AC_CHECK_LIB(sunmath, acosp,
+			[AC_CHECK_LIB(sunperf, $sc_blas_func,
+                                [BLAS_LIBS="-xlic_lib=sunperf -lsunmath"
+                                 sc_blas_ok=yes],[],[-lsunmath])])
+	fi
+fi
+
+# BLAS in SCSL library?  (SGI/Cray Scientific Library)
+if test "x$sc_blas_ok" = xno; then
+	AC_CHECK_LIB(scs, $sc_blas_func, [sc_blas_ok=yes; BLAS_LIBS="-lscs"])
+fi
+
+# BLAS in SGIMATH library?
+if test "x$sc_blas_ok" = xno; then
+	AC_CHECK_LIB(complib.sgimath, $sc_blas_func,
+		     [sc_blas_ok=yes; BLAS_LIBS="-lcomplib.sgimath"])
+fi
+
+# BLAS in IBM ESSL library? (requires generic BLAS lib, too)
+if test "x$sc_blas_ok" = xno; then
+	AC_CHECK_LIB(blas, $sc_blas_func,
+		[AC_CHECK_LIB(essl, $sc_blas_func,
+			[sc_blas_ok=yes; BLAS_LIBS="-lessl -lblas"],
+			[], [-lblas $FLIBS])])
+fi
+
+# Generic Mac OS X library?
+if test "x$sc_blas_ok" = xno; then
+	save_LIBS="$LIBS"; LIBS="-framework Accelerate $LIBS"
+	AC_CHECK_FUNC($sc_blas_func, [sc_blas_ok=yes
+                               BLAS_LIBS="-framework Accelerate"])
+	LIBS="$save_LIBS"
+fi
+
+# Generic BLAS library?
+if test "x$sc_blas_ok" = xno; then
+	AC_CHECK_LIB(blas, $sc_blas_func, [sc_blas_ok=yes; BLAS_LIBS="-lblas"])
+fi
+
+dnl AC_SUBST(BLAS_LIBS)
+
+fi # If the user specified library wasn't found, we skipped the remaining
+   # checks.
+
+LIBS="$sc_blas_save_LIBS"
+BLAS_FLIBS=
+
+# Test link a BLAS program
+if test "x$sc_blas_ok" = xyes ; then
+    dnl Link without FLIBS first
+    sc_blas_save_run_LIBS="$LIBS"
+    LIBS="$BLAS_LIBS $LIBS"
+    SC_BLAS_LINK([ without FLIBS])
+    LIBS="$sc_blas_save_run_LIBS"
+
+    if test "x$sc_blas_ok" = xno ; then
+        dnl Link with FLIBS it didn't work without
+        sc_blas_save_run_LIBS="$LIBS"
+        LIBS="$BLAS_LIBS $LIBS $FLIBS"
+        sc_blas_ok=yes
+        SC_BLAS_LINK([ with FLIBS])
+        LIBS="$sc_blas_save_run_LIBS"
+        BLAS_FLIBS="$FLIBS"
+    fi
+fi
+dnl Now BLAS_FLIBS may be set or not
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test "x$sc_blas_ok" = xyes ; then
+        ifelse([$3],,
+               [AC_DEFINE(HAVE_BLAS,1,[Define if you have a BLAS library.])],[$3])
+        :
+elif test "x$sc_blas_ok" != xdisable ; then
+        sc_blas_ok=no
+        $4
+fi
+
+])
diff --git a/sc/config/sc_builtin.m4 b/sc/config/sc_builtin.m4
new file mode 100644
index 0000000..f5f7a14
--- /dev/null
+++ b/sc/config/sc_builtin.m4
@@ -0,0 +1,71 @@
+
+dnl sc_builtin.m4 - custom macros for distributing third-party software
+dnl
+dnl This file is part of the SC Library.
+dnl The SC library provides support for parallel scientific applications.
+dnl
+dnl Copyright (C) 2008,2009 Carsten Burstedde, Lucas Wilcox.
+
+dnl Documentation for macro names: brackets indicate optional arguments
+
+dnl SC_BUILTIN_GETOPT_PREFIX(PREFIX)
+dnl This function only activates if PREFIX_WITH_GETOPT is "yes".
+dnl This function checks if getopt_long can be compiled.
+dnl The shell variable PREFIX_PROVIDE_GETOPT is set to "yes" or "no".
+dnl Both a define and automake conditional are set.
+dnl
+AC_DEFUN([SC_BUILTIN_GETOPT_PREFIX],
+[
+$1_PROVIDE_GETOPT="no"
+AC_MSG_CHECKING([for getopt])
+AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[#include <getopt.h>]], [[
+int oi;
+struct option lo;
+getopt_long (0, 0, "abc:", &lo, &oi);
+]])], [AC_MSG_RESULT([successful])], [
+    AC_MSG_RESULT([failed])
+    AC_MSG_NOTICE([did not find getopt. Activating builtin])
+    $1_PROVIDE_GETOPT="yes"
+    AC_DEFINE([PROVIDE_GETOPT], 1, [Use builtin getopt])
+])
+AM_CONDITIONAL([$1_PROVIDE_GETOPT], [test "$$1_PROVIDE_GETOPT" = "yes"])
+])
+AC_DEFUN([SC_BUILTIN_GETOPT], [SC_BUILTIN_GETOPT_PREFIX([SC])])
+
+dnl SC_BUILTIN_OBSTACK_PREFIX(PREFIX)
+dnl This function only activates if PREFIX_WITH_OBSTACK is "yes".
+dnl This function checks if a simple obstack program can be compiled.
+dnl The shell variable PREFIX_PROVIDE_OBSTACK is set to "yes" or "no".
+dnl Both a define and automake conditional are set.
+dnl
+AC_DEFUN([SC_BUILTIN_OBSTACK_PREFIX],
+[
+$1_PROVIDE_OBSTACK="no"
+AC_MSG_CHECKING([for obstack])
+AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[#include <obstack.h>]], [[
+struct obstack ob;
+static void        *(*obstack_chunk_alloc) (size_t) = 0;
+static void         (*obstack_chunk_free) (void *) = 0;
+obstack_init (&ob);
+obstack_free (&ob, 0);
+]])], [AC_MSG_RESULT([successful])], [
+    AC_MSG_RESULT([failed])
+    AC_MSG_NOTICE([did not find obstack. Activating builtin])
+    $1_PROVIDE_OBSTACK="yes"
+    AC_DEFINE([PROVIDE_OBSTACK], 1, [Use builtin obstack])
+])
+AM_CONDITIONAL([$1_PROVIDE_OBSTACK], [test "$$1_PROVIDE_OBSTACK" = "yes"])
+])
+AC_DEFUN([SC_BUILTIN_OBSTACK], [SC_BUILTIN_OBSTACK_PREFIX([SC])])
+
+dnl SC_BUILTIN_ALL_PREFIX(PREFIX)
+dnl Aggregate all checks from this file for convenience.
+dnl
+AC_DEFUN([SC_BUILTIN_ALL_PREFIX],
+[
+SC_BUILTIN_GETOPT_PREFIX([$1])
+SC_BUILTIN_OBSTACK_PREFIX([$1])
+])
+AC_DEFUN([SC_BUILTIN_ALL], [SC_BUILTIN_ALL_PREFIX([SC])])
diff --git a/sc/config/sc_c_check_flag.m4 b/sc/config/sc_c_check_flag.m4
new file mode 100644
index 0000000..783cd72
--- /dev/null
+++ b/sc/config/sc_c_check_flag.m4
@@ -0,0 +1,90 @@
+# ===========================================================================
+#            http://autoconf-archive.cryp.to/ax_c_check_flag.html
+# and renamed by Carsten Burstedde <carsten at ices.utexas.edu>
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   SC_C_CHECK_FLAG(FLAG-TO-CHECK,
+#                   [PROLOGUE],[BODY],[ACTION-IF-SUCCESS],[ACTION-IF-FAILURE])
+#
+# DESCRIPTION
+#
+#   This macro tests if the C compiler supports the flag FLAG-TO-CHECK. If
+#   successfull execute ACTION-IF-SUCCESS otherwise ACTION-IF-FAILURE.
+#   PROLOGUE and BODY are optional and should be used as in AC_LANG_PROGRAM
+#   macro.
+#
+#   This code is inspired from KDE_CHECK_COMPILER_FLAG macro. Thanks to
+#   Bogdan Drozdowski <bogdandr at op.pl> for testing and bug fixes.
+#
+# LAST MODIFICATION
+#
+#   2009-02-09
+#
+# COPYLEFT
+#
+#   Copyright (c) 2008 Francesco Salvestrini <salvestrini at users.sourceforge.net>
+#
+#   This program is free software; you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation; either version 2 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Macro Archive. When you make and
+#   distribute a modified version of the Autoconf Macro, you may extend this
+#   special exception to the GPL to apply to your modified version as well.
+
+AC_DEFUN([SC_C_CHECK_FLAG],[
+  AC_PREREQ([2.61])
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_PROG_SED])
+
+  flag=`echo "$1" | $SED 'y% .=/+-(){}<>:*,%_______________%'`
+
+  AC_CACHE_CHECK([whether the C compiler accepts the $1 flag],
+    [sc_cv_c_check_flag_$flag],[
+
+    AC_LANG_PUSH([C])
+
+    save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS $1"
+    AC_COMPILE_IFELSE([
+      AC_LANG_PROGRAM([$2],[$3])
+    ],[
+      eval "sc_cv_c_check_flag_$flag=yes"
+    ],[
+      eval "sc_cv_c_check_flag_$flag=no"
+    ])
+
+    CFLAGS="$save_CFLAGS"
+
+    AC_LANG_POP
+
+  ])
+
+  AS_IF([eval "test \"`echo '$sc_cv_c_check_flag_'$flag`\" = yes"],[
+    :
+    $4
+  ],[
+    :
+    $5
+  ])
+])
diff --git a/sc/config/sc_c_version.m4 b/sc/config/sc_c_version.m4
new file mode 100644
index 0000000..663ac4f
--- /dev/null
+++ b/sc/config/sc_c_version.m4
@@ -0,0 +1,89 @@
+# ===========================================================================
+#            From: http://autoconf-archive.cryp.to/ax_gcc_version.html
+# and renamed by Carsten Burstedde <carsten at ices.utexas.edu>
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   SC_C_VERSION  (Extension of AX_GCC_VERSION to more C compilers)
+#
+# DESCRIPTION
+#
+#   This macro retrieves the cc version and returns it in the C_VERSION
+#   variable if available, an empty string otherwise.
+#
+# LAST MODIFICATION
+#
+#   2009-02-09
+#
+# COPYLEFT
+#
+#   Copyright (c) 2008 Lucas Wilcox <lucasw at ices.utexas.edu>
+#   Copyright (c) 2008 Francesco Salvestrini <salvestrini at users.sourceforge.net>
+#
+#   This program is free software; you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation; either version 2 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Macro Archive. When you make and
+#   distribute a modified version of the Autoconf Macro, you may extend this
+#   special exception to the GPL to apply to your modified version as well.
+
+AC_DEFUN([SC_C_VERSION], [
+  C_VERSION=""
+  AS_IF([test "x$C_VERSION" = "x"],[
+    SC_C_CHECK_FLAG([-V],[],[],[
+      sc_pgcc_version_option=yes
+    ],[
+      sc_pgcc_version_option=no
+    ])
+    AS_IF([test "x$sc_pgcc_version_option" != "xno"],[
+      AC_CACHE_CHECK([pgcc version],[sc_cv_pgcc_version],[
+        # The sed part removes all new lines
+        sc_cv_pgcc_version="`$CC -V 2>/dev/null | sed -e :a -e '$!N; s/\n/ /; ta'`"
+        AS_IF([test "x$sc_cv_pgcc_version" = "x"],[
+          sc_cv_pgcc_version=""
+          ])
+        ])
+      C_VERSION=$sc_cv_pgcc_version
+    ])
+  ])
+
+  AS_IF([test "x$C_VERSION" = "x"],[
+    SC_C_CHECK_FLAG([-dumpversion],[],[],[
+      sc_gcc_version_option=yes
+    ],[
+      sc_gcc_version_option=no
+    ])
+    AS_IF([test "x$sc_gcc_version_option" != "xno"],[
+      AC_CACHE_CHECK([gcc version],[sc_cv_gcc_version],[
+        # The sed part removes all new lines
+        sc_cv_gcc_version="`$CC -dumpversion | sed -e :a -e '$!N; s/\n/ /; ta'`"
+        AS_IF([test "x$sc_cv_gcc_version" = "x"],[
+          sc_cv_gcc_version=""
+        ])
+      ])
+      C_VERSION=$sc_cv_gcc_version
+    ])
+  ])
+
+  AC_SUBST([C_VERSION])
+])
diff --git a/sc/config/sc_cuda.m4 b/sc/config/sc_cuda.m4
new file mode 100644
index 0000000..9e37a68
--- /dev/null
+++ b/sc/config/sc_cuda.m4
@@ -0,0 +1,42 @@
+
+dnl This macro attempts to insert cuda rules into the Makefiles.
+dnl This happens only when --with-nvcc[=NAME] is used on the configure line.
+dnl
+dnl SC_CUDA(PREFIX)
+dnl Figure out if cuda should be used.
+dnl Defines automake conditional SC_CUDA and activates Makefile rule.
+dnl
+AC_DEFUN([SC_CUDA],
+[
+# check --with-nvcc[=NAME] command line option
+SC_ARG_WITH_PREFIX([nvcc], [enable CUDA and specify compiler (default: nvcc)],
+                   [CUDA], [$1], [[[=NAME]]])
+if test "$withval" != "no" ; then
+  # determine name of CUDA compiler in variable PREFIX_NVCC_NAME
+  $1_NVCC_NAME="nvcc"
+  if test "$withval" != "yes" ; then
+    $1_NVCC_NAME="$withval"
+  fi
+
+  # find location of CUDA compiler in PATH
+  AC_PATH_PROG([$1_NVCC], [nvcc], [no])
+  if test "$$1_NVCC" = "no" ; then
+    AC_MSG_ERROR([CUDA compiler $1_NVCC_NAME not found])
+  fi
+
+  # cuda does not yet work with libtool
+  if test -n "$$1_ENABLE_SHARED" -a "$$1_ENABLE_SHARED" != "no" ; then
+    AC_MSG_ERROR([--with-nvcc does not yet work with --enable-shared])
+  fi
+
+  # print some variables
+  AC_MSG_NOTICE([$1_NVCC_NAME is $$1_NVCC_NAME])
+  AC_MSG_NOTICE([$1_NVCC is $$1_NVCC])
+  AC_MSG_NOTICE([NVCCFLAGS is $NVCCFLAGS])
+  AC_MSG_NOTICE([NVCCLIBS is $NVCCLIBS])
+fi
+
+AC_SUBST([$1_NVCC])
+AC_SUBST([NVCCFLAGS])
+AC_SUBST([NVCCLIBS])
+])
diff --git a/sc/config/sc_include.m4 b/sc/config/sc_include.m4
new file mode 100644
index 0000000..4d8e347
--- /dev/null
+++ b/sc/config/sc_include.m4
@@ -0,0 +1,305 @@
+
+dnl sc_include.m4 - general custom macros
+dnl
+dnl This file is part of the SC Library.
+dnl The SC library provides support for parallel scientific applications.
+dnl
+dnl Copyright (C) 2008,2009 Carsten Burstedde, Lucas Wilcox.
+
+dnl Documentation for macro names: brackets indicate optional arguments
+
+dnl SC_VERSION(PREFIX)
+dnl Expose major, minor, and point version numbers as CPP defines.
+dnl Also creates a makefile variable PACKAGE_PREFIX with value PREFIX.
+dnl
+AC_DEFUN([SC_VERSION],
+[
+  AX_SPLIT_VERSION
+  AC_DEFINE_UNQUOTED([VERSION_MAJOR],[$AX_MAJOR_VERSION],[Package major version])
+  AC_DEFINE_UNQUOTED([VERSION_MINOR],[$AX_MINOR_VERSION],[Package minor version])
+  AC_DEFINE_UNQUOTED([VERSION_POINT],[$AX_POINT_VERSION],[Package point version])
+  AC_SUBST([PACKAGE_PREFIX], [$1])
+])
+
+dnl SC_ARG_ENABLE_PREFIX(NAME, COMMENT, TOKEN, PREFIX, HELPEXTRA)
+dnl Check for --enable/disable-NAME using shell variable PREFIX_ENABLE_TOKEN
+dnl If shell variable is set beforehand it overrides the option
+dnl If enabled, define TOKEN to 1 and set conditional PREFIX_ENABLE_TOKEN
+dnl Default is disabled
+dnl
+AC_DEFUN([SC_ARG_ENABLE_PREFIX],
+[
+AC_ARG_ENABLE([$1],
+              [AS_HELP_STRING([--enable-$1$5], [$2])],,
+              [enableval=no])
+if test "x$enableval" != xno ; then
+  AC_DEFINE([$3], 1, [DEPRECATED (use $4_ENABLE_$3 instead)])
+  AC_DEFINE([ENABLE_$3], 1, [$2])
+fi
+AM_CONDITIONAL([$4_ENABLE_$3], [test "x$enableval" != xno])
+$4_ENABLE_$3="$enableval"
+])
+AC_DEFUN([SC_ARG_ENABLE],
+         [SC_ARG_ENABLE_PREFIX([$1], [$2], [$3], [SC], [$4])])
+
+dnl SC_ARG_DISABLE_PREFIX(NAME, COMMENT, TOKEN, PREFIX, HELPEXTRA)
+dnl Check for --enable/disable-NAME using shell variable PREFIX_ENABLE_TOKEN
+dnl If shell variable is set beforehand it overrides the option
+dnl If enabled, define TOKEN to 1 and set conditional PREFIX_ENABLE_TOKEN
+dnl Default is enabled
+dnl
+AC_DEFUN([SC_ARG_DISABLE_PREFIX],
+[
+AC_ARG_ENABLE([$1],
+              [AS_HELP_STRING([--disable-$1$5], [$2])],,
+              [enableval=yes])
+if test "x$enableval" != xno ; then
+  AC_DEFINE([$3], 1, [DEPRECATED (use $4_ENABLE_$3 instead)])
+  AC_DEFINE([ENABLE_$3], 1, [Undefine if: $2])
+fi
+AM_CONDITIONAL([$4_ENABLE_$3], [test "x$enableval" != xno])
+$4_ENABLE_$3="$enableval"
+])
+AC_DEFUN([SC_ARG_DISABLE],
+         [SC_ARG_DISABLE_PREFIX([$1], [$2], [$3], [SC], [$4])])
+
+dnl SC_ARG_WITH_PREFIX(NAME, COMMENT, TOKEN, PREFIX, HELPEXTRA)
+dnl Check for --with/without-NAME using shell variable PREFIX_WITH_TOKEN
+dnl If shell variable is set beforehand it overrides the option
+dnl If with, define TOKEN to 1 and set conditional PREFIX_WITH_TOKEN
+dnl Default is without
+dnl
+AC_DEFUN([SC_ARG_WITH_PREFIX],
+[
+AC_ARG_WITH([$1],
+            [AS_HELP_STRING([--with-$1$5], [$2])],,
+            [withval=no])
+if test "x$withval" != xno ; then
+  AC_DEFINE([$3], 1, [DEPRECATED (use $4_WITH_$3 instead)])
+  AC_DEFINE([WITH_$3], 1, [$2])
+fi
+AM_CONDITIONAL([$4_WITH_$3], [test "x$withval" != xno])
+$4_WITH_$3="$withval"
+])
+AC_DEFUN([SC_ARG_WITH],
+         [SC_ARG_WITH_PREFIX([$1], [$2], [$3], [SC], [$4])])
+
+dnl SC_ARG_WITHOUT_PREFIX(NAME, COMMENT, TOKEN, PREFIX, HELPEXTRA)
+dnl Check for --with/without-NAME using shell variable PREFIX_WITH_TOKEN
+dnl If shell variable is set beforehand it overrides the option
+dnl If with, define TOKEN to 1 and set conditional PREFIX_WITH_TOKEN
+dnl Default is with
+dnl
+AC_DEFUN([SC_ARG_WITHOUT_PREFIX],
+[
+AC_ARG_WITH([$1],
+            [AS_HELP_STRING([--without-$1$5], [$2])],,
+            [withval=yes])
+if test "x$withval" != xno ; then
+  AC_DEFINE([$3], 1, [DEPRECATED (use $4_WITH_$3 instead)])
+  AC_DEFINE([WITH_$3], 1, [Undefine if: $2])
+fi
+AM_CONDITIONAL([$4_WITH_$3], [test "x$withval" != xno])
+$4_WITH_$3="$withval"
+])
+AC_DEFUN([SC_ARG_WITHOUT],
+         [SC_ARG_WITHOUT_PREFIX([$1], [$2], [$3], [SC], [$4])])
+
+dnl SC_REQUIRE_LIB(LIBRARY LIST, FUNCTION)
+dnl Check for FUNCTION in LIBRARY, exit with error if not found
+dnl
+AC_DEFUN([SC_REQUIRE_LIB],
+    [AC_SEARCH_LIBS([$2], [$1],,
+      [AC_MSG_ERROR([Could not find function $2 in $1])])])
+
+dnl SC_CHECK_LIB(LIBRARY LIST, FUNCTION, TOKEN, PREFIX)
+dnl Check for FUNCTION first as is, then in each of the libraries.
+dnl Set shell variable PREFIX_HAVE_TOKEN to nonempty if found.
+dnl Call AM_CONDITIONAL with PREFIX_HAVE_TOKEN.
+dnl Call AC_DEFINE with HAVE_TOKEN if found.
+AC_DEFUN([SC_CHECK_LIB], [
+AC_SEARCH_LIBS([$2], [$1])
+AM_CONDITIONAL([$4_HAVE_$3], [test "x$ac_cv_search_$2" != xno])
+$4_HAVE_$3=
+if test "x$ac_cv_search_$2" != xno ; then
+AC_DEFINE([HAVE_$3], [1], [Have we found function $2.])
+$4_HAVE_$3=yes
+fi
+])
+
+dnl SC_REQUIRE_FUNCS(FUNCTION LIST)
+dnl Check for all functions in FUNCTION LIST, exit with error if not found
+dnl
+AC_DEFUN([SC_REQUIRE_FUNCS],
+[
+m4_foreach_w([sc_thefunc], [$1],
+             [AC_CHECK_FUNC([sc_thefunc], ,
+                            [AC_MSG_ERROR([\
+Could not find function sc_thefunc])])])
+])
+
+dnl SC_DETERMINE_INSTALL(PREFIX)
+dnl This function throws an error if the variable PREFIX_DIR does not exist.
+dnl Looks for PREFIX_DIR/{include,lib,bin} to determine installation status.
+dnl Set the shell variable PREFIX_INSTALL to "yes" or "no".
+dnl
+AC_DEFUN([SC_DETERMINE_INSTALL],
+[
+if test ! -d "$$1_DIR" ; then
+  AC_MSG_ERROR([Directory "$$1_DIR" does not exist])
+fi
+if test -d "$$1_DIR/include" || test -d "$$1_DIR/lib" || \
+   test -d "$$1_DIR/bin" || test -d "$$1_DIR/share/aclocal" ; then
+  $1_INSTALL=yes
+else
+  $1_INSTALL=no
+fi
+])
+
+dnl SC_DETERMINE_INCLUDE_PATH(PREFIX, CPPFLAGS)
+dnl This function expects the variable PREFIX_DIR to exist.
+dnl Looks for PREFIX_DIR/include and then PREFIX_DIR/src.
+dnl If neither is found, throws an error.
+dnl Otherwise, set the shell variable PREFIX_CPPFLAGS to -I<dir> CPPFLAGS.
+dnl
+AC_DEFUN([SC_DETERMINE_INCLUDE_PATH],
+[
+$1_INC="$$1_DIR/include"
+if test ! -d "$$1_INC" ; then
+  $1_INC="$$1_DIR/src"
+fi
+if test ! -d "$$1_INC" ; then
+  AC_MSG_ERROR([Include directories based on $$1_DIR not found])
+fi
+$1_CPPFLAGS="-I$$1_INC $2"
+])
+
+dnl SC_DETERMINE_LIBRARY_PATH(PREFIX, LIBS)
+dnl This function expects the variable PREFIX_DIR to exist.
+dnl Looks for PREFIX_DIR/lib and then PREFIX_DIR/src.
+dnl If neither is found, throws an error.
+dnl Otherwise, set the shell variable PREFIX_LDADD to -L<dir> LIBS.
+dnl
+AC_DEFUN([SC_DETERMINE_LIBRARY_PATH],
+[
+$1_LIB="$$1_DIR/lib"
+if test ! -d "$$1_LIB" ; then
+  $1_LIB="$$1_DIR/src"
+fi
+if test ! -d "$$1_LIB" ; then
+  AC_MSG_ERROR([Library directories based on $$1_DIR not found])
+fi
+$1_LDADD="-L$$1_LIB $2"
+])
+
+dnl SC_DETERMINE_CONFIG_PATH(PREFIX)
+dnl This function expects the variable PREFIX_DIR to exist.
+dnl Looks for PREFIX_DIR/share/aclocal and then PREFIX_DIR/src.
+dnl If neither is found, throws an error.
+dnl Sets shell variables PREFIX_CONFIG and PREFIX_AMFLAGS.
+dnl
+AC_DEFUN([SC_DETERMINE_CONFIG_PATH],
+[
+$1_CONFIG="$$1_DIR/share/aclocal"
+if test ! -d "$$1_CONFIG" ; then
+  $1_CONFIG="$$1_DIR/config"
+fi
+if test ! -d "$$1_CONFIG" ; then
+  AC_MSG_ERROR([Config directories based on $$1_DIR not found])
+fi
+$1_AMFLAGS="-I $$1_CONFIG"
+])
+
+dnl SC_CHECK_BLAS_LAPACK(PREFIX)
+dnl This function uses the macros SC_BLAS and SC_LAPACK.
+dnl It requires previous configure macros for F77 support,
+dnl which are called by SC_MPI_CONFIG/SC_MPI_ENGAGE.
+dnl
+AC_DEFUN([SC_CHECK_BLAS_LAPACK],
+[
+
+dgemm=;AC_F77_FUNC(dgemm)
+if test "x$dgemm" = xunknown ; then dgemm=dgemm_ ; fi
+
+AC_MSG_NOTICE([Checking BLAS])
+SC_BLAS([$1], [$dgemm],
+        [AC_DEFINE([WITH_BLAS], 1, [Define to 1 if BLAS is used])],
+        [AC_MSG_ERROR([[\
+Cannot find BLAS library, specify a path using LIBS=-L<DIR> (ex.\
+ LIBS=-L/usr/path/lib) or a specific library using BLAS_LIBS=DIR/LIB\
+ (for example BLAS_LIBS=/usr/path/lib/libcxml.a)]])])
+
+# at this point $sc_blas_ok is either of: yes disable
+if test "x$sc_blas_ok" = xdisable ; then
+        AC_MSG_NOTICE([Not using BLAS])
+fi
+AM_CONDITIONAL([$1_WITH_BLAS], [test "x$sc_blas_ok" = xyes])
+
+dgecon=;AC_F77_FUNC(dgecon)
+if test "x$dgecon" = xunknown ; then dgecon=dgecon_ ; fi
+
+AC_MSG_NOTICE([Checking LAPACK])
+SC_LAPACK([$1], [$dgecon],
+          [AC_DEFINE([WITH_LAPACK], 1, [Define to 1 if LAPACK is used])],
+          [AC_MSG_ERROR([[\
+Cannot find LAPACK library, specify a path using LIBS=-L<DIR> (ex.\
+ LIBS=-L/usr/path/lib) or a specific library using LAPACK_LIBS=DIR/LIB\
+ (for example LAPACK_LIBS=/usr/path/lib/libcxml.a)]])])
+
+# at this point $sc_lapack_ok is either of: yes disable
+if test "x$sc_lapack_ok" = xdisable ; then
+        AC_MSG_NOTICE([Not using LAPACK])
+fi
+AM_CONDITIONAL([$1_WITH_LAPACK], [test "x$sc_lapack_ok" = xyes])
+
+# Append the necessary blas/lapack and fortran libraries to LIBS
+LIBS="$LAPACK_LIBS $BLAS_LIBS $LIBS $LAPACK_FLIBS $BLAS_FLIBS"
+])
+
+dnl SC_CHECK_LIBRARIES(PREFIX)
+dnl This macro bundles the checks for all libraries and link tests
+dnl that are required by libsc.  It can be used by other packages that
+dnl link to libsc to add appropriate options to LIBS.
+dnl
+AC_DEFUN([SC_CHECK_LIBRARIES],
+[
+SC_REQUIRE_LIB([m], [fabs])
+SC_CHECK_LIB([z], [adler32_combine], [ZLIB], [$1])
+SC_CHECK_LIB([lua52 lua5.2 lua51 lua5.1 lua lua5], [lua_createtable],
+	     [LUA], [$1])
+SC_CHECK_BLAS_LAPACK([$1])
+SC_BUILTIN_ALL_PREFIX([$1])
+SC_CHECK_PTHREAD([$1])
+dnl SC_CUDA([$1])
+])
+
+dnl SC_AS_SUBPACKAGE(PREFIX)
+dnl Call from a package that is using libsc as a subpackage.
+dnl Sets PREFIX_DIST_DENY=yes if sc is make install'd.
+dnl
+AC_DEFUN([SC_AS_SUBPACKAGE],
+         [SC_ME_AS_SUBPACKAGE([$1], [m4_tolower([$1])], [SC], [sc])])
+
+dnl SC_FINAL_MESSAGES(PREFIX)
+dnl This macro prints messages at the end of the configure run.
+dnl
+AC_DEFUN([SC_FINAL_MESSAGES],
+[
+if test "x$$1_HAVE_ZLIB" = x ; then
+AC_MSG_NOTICE([- $1 -------------------------------------------------
+We did not find a recent zlib containing the function adler32_combine.
+This is OK if the following does not matter to you:
+Calling any sc functions that rely on zlib will abort your program.
+These functions include sc_array_checksum and sc_vtk_write_compressed.
+You can fix this by compiling a working zlib and pointing LIBS to it.
+])
+fi
+if test "x$$1_HAVE_LUA" = x ; then
+AC_MSG_NOTICE([- $1 -------------------------------------------------
+We did not find a recent lua containing the function lua_createtable.
+This is OK if the following does not matter to you:
+Including sc_lua.h in your code will abort the compilation.
+You can fix this by compiling a working lua and pointing LIBS to it.
+])
+fi
+])
diff --git a/sc/config/sc_lapack.m4 b/sc/config/sc_lapack.m4
new file mode 100644
index 0000000..48d082b
--- /dev/null
+++ b/sc/config/sc_lapack.m4
@@ -0,0 +1,200 @@
+dnl This is a modified version of the Teuchos config dir from Trilinos
+dnl with the following license.
+dnl
+dnl ***********************************************************************
+dnl
+dnl                    Teuchos: Common Tools Package
+dnl                 Copyright (2004) Sandia Corporation
+dnl
+dnl Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
+dnl license for use of this work by or on behalf of the U.S. Government.
+dnl
+dnl This library is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU Lesser General Public License as
+dnl published by the Free Software Foundation; either version 2.1 of the
+dnl License, or (at your option) any later version.
+dnl
+dnl This library is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+dnl Lesser General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU Lesser General Public
+dnl License along with this library; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+dnl USA
+dnl Questions? Contact Michael A. Heroux (maherou at sandia.gov)
+dnl
+dnl ***********************************************************************
+dnl
+dnl @synopsis SC_LAPACK(PREFIX, DGECON_FUNCTION,
+dnl                     [ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+dnl
+dnl This macro looks for a library that implements the LAPACK
+dnl linear-algebra interface (see http://www.netlib.org/lapack/).
+dnl On success, it sets the LAPACK_LIBS output variable to
+dnl hold the requisite library linkages.
+dnl
+dnl To link with LAPACK, you should link with:
+dnl
+dnl     $LAPACK_LIBS $BLAS_LIBS $LIBS $FLIBS
+dnl
+dnl in that order.  BLAS_LIBS is the output variable of the SC_BLAS
+dnl macro, called automatically.  FLIBS is the output variable of the
+dnl AC_F77_LIBRARY_LDFLAGS macro (called if necessary by SC_BLAS),
+dnl and is sometimes necessary in order to link with F77 libraries.
+dnl Users will also need to use AC_F77_DUMMY_MAIN (see the autoconf
+dnl manual), for the same reason.
+dnl
+dnl The user may also use --with-lapack=<lib> in order to use some
+dnl specific LAPACK library <lib>.  In order to link successfully,
+dnl however, be aware that you will probably need to use the same
+dnl Fortran compiler (which can be set via the F77 env. var.) as
+dnl was used to compile the LAPACK and BLAS libraries.
+dnl
+dnl ACTION-IF-FOUND is a list of shell commands to run if a LAPACK
+dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands
+dnl to run it if it is not found.  If ACTION-IF-FOUND is not specified,
+dnl the default action will define HAVE_LAPACK.
+dnl
+dnl @version $Id: acx_lapack.m4,v 1.3 2006/04/21 02:29:27 jmwille Exp $
+dnl @author Steven G. Johnson <stevenj at alum.mit.edu>
+dnl edited by Jim Willenbring <jmwille at sandia.gov> to check for sgecon
+dnl rather than cheev because by default (as of 8-13-2002) Trilinos
+dnl does not build the complex portions of the lapack library.  Edited
+dnl again on 5-13-2004 to check for dgecon instead of sgecon.
+dnl Edited by Jim Willenbring on 4-17-2006 to stop looking for LAPACK if
+dnl a specific LAPACK library specified by a user cannot be used.
+
+dnl Edited by Carsten Burstedde <carsten at ices.utexas.edu>
+dnl Expect the F77_ autoconf macros to be called outside of this file.
+dnl Take as argument a mangled DGECON function to check for.
+dnl This way the SC_LAPACK macro can be called multiple times
+dnl with different Fortran environments to minimize F77 dependencies.
+dnl Replaced obsolete AC_TRY_LINK_FUNC macro.
+
+dnl Subroutine to link a program using lapack
+dnl SC_LAPACK_LINK (<added to CHECKING message>)
+AC_DEFUN([SC_LAPACK_LINK], [
+        AC_MSG_CHECKING([for LAPACK by linking$1])
+        AC_LINK_IFELSE([AC_LANG_PROGRAM(dnl
+[[
+#ifdef __cplusplus
+extern "C"
+void $sc_lapack_func (char *, int *, double *, int *, double *,
+                      double *, double *, int *, int *);
+#endif
+]], [[
+int     i = 1, info = 0, iwork[1];
+double  anorm = 1., rcond;
+double  A = 1., work[4];
+$sc_lapack_func ("1", &i, &A, &i, &anorm, &rcond, work, iwork, &info);
+]])],
+[AC_MSG_RESULT([successful])],
+[AC_MSG_RESULT([failed]); sc_lapack_ok=no])
+])
+
+dnl The first argument of this macro should be the package prefix.
+dnl The second argument of this macro should be a mangled DGECON function.
+AC_DEFUN([SC_LAPACK], [
+AC_REQUIRE([SC_BLAS])
+sc_lapack_ok=no
+user_spec_lapack_failed=no
+
+AC_ARG_WITH([lapack], [AS_HELP_STRING([--with-lapack=<lib>],
+            [change default LAPACK library to <lib>
+             or specify --without-lapack to use no LAPACK at all])],,
+	     [withval=yes])
+case $withval in
+        yes | "") ;;
+        no) sc_lapack_ok=disable ;;
+        -* | */* | *.a | *.so | *.so.* | *.o) LAPACK_LIBS="$withval" ;;
+        *) LAPACK_LIBS="-l$withval" ;;
+esac
+
+dnl Expect the mangled DGECON function name to be in $2.
+sc_lapack_func="$2"
+
+# We cannot use LAPACK if BLAS is not found
+if test "x$sc_blas_ok" = xdisable ; then
+        sc_lapack_ok=disable
+elif test "x$sc_blas_ok" != xyes; then
+        sc_lapack_ok=noblas
+fi
+
+# First, check LAPACK_LIBS environment variable
+if test "x$sc_lapack_ok" = xno; then
+if test "x$LAPACK_LIBS" != x; then
+        save_LIBS="$LIBS"; LIBS="$LAPACK_LIBS $BLAS_LIBS $LIBS $FLIBS"
+        AC_MSG_CHECKING([for $sc_lapack_func in $LAPACK_LIBS])
+	AC_LINK_IFELSE([AC_LANG_CALL([], [$sc_lapack_func])],
+                       [sc_lapack_ok=yes], [user_spec_lapack_failed=yes])
+        AC_MSG_RESULT($sc_lapack_ok)
+        LIBS="$save_LIBS"
+        if test "x$sc_lapack_ok" = xno; then
+                LAPACK_LIBS=""
+        fi
+fi
+fi
+
+# If the user specified a LAPACK library that could not be used we will
+# halt the search process rather than risk finding a LAPACK library that
+# the user did not specify.
+
+if test "x$user_spec_lapack_failed" != xyes; then
+
+# LAPACK linked to by default?  (is sometimes included in BLAS lib)
+if test "x$sc_lapack_ok" = xno; then
+        save_LIBS="$LIBS"; LIBS="$BLAS_LIBS $LIBS $FLIBS"
+        AC_CHECK_FUNC($sc_lapack_func, [sc_lapack_ok=yes])
+        LIBS="$save_LIBS"
+fi
+
+# Generic LAPACK library?
+for lapack in lapack lapack_rs6k; do
+        if test "x$sc_lapack_ok" = xno; then
+                save_LIBS="$LIBS"; LIBS="$BLAS_LIBS $LIBS"
+                AC_CHECK_LIB($lapack, $sc_lapack_func,
+                    [sc_lapack_ok=yes; LAPACK_LIBS="-l$lapack"], [], [$FLIBS])
+                LIBS="$save_LIBS"
+        fi
+done
+
+dnl AC_SUBST(LAPACK_LIBS)
+
+fi # If the user specified library wasn't found, we skipped the remaining
+   # checks.
+
+LAPACK_FLIBS=
+
+# Test link and run a LAPACK program
+if test "x$sc_lapack_ok" = xyes ; then
+    dnl Link without FLIBS, or with FLIBS if required by BLAS
+    sc_lapack_save_run_LIBS="$LIBS"
+    LIBS="$LAPACK_LIBS $BLAS_LIBS $LIBS $BLAS_FLIBS"
+    SC_LAPACK_LINK([ w/ BLAS_FLIBS but w/o FLIBS])
+    LIBS="$sc_lapack_save_run_LIBS"
+
+    if test "x$sc_lapack_ok" = xno && test "x$BLAS_FLIBS" = x ; then
+        dnl Link with FLIBS it didn't work without
+        sc_lapack_save_run_LIBS="$LIBS"
+        LIBS="$LAPACK_LIBS $BLAS_LIBS $LIBS $FLIBS"
+        sc_lapack_ok=yes
+        SC_LAPACK_LINK([ with FLIBS])
+        LIBS="$sc_lapack_save_run_LIBS"
+        LAPACK_FLIBS="$FLIBS"
+    fi
+fi
+dnl Now at most one of BLAS_FLIBS and LAPACK_FLIBS may be set, but not both
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test "x$sc_lapack_ok" = xyes; then
+        ifelse([$3],,
+               [AC_DEFINE(HAVE_LAPACK,1,[Define if you have LAPACK library.])],[$3])
+        :
+elif test "x$sc_lapack_ok" != xdisable ; then
+        sc_lapack_ok=no
+        $4
+fi
+
+])
diff --git a/sc/config/sc_lint.m4 b/sc/config/sc_lint.m4
new file mode 100644
index 0000000..1e24921
--- /dev/null
+++ b/sc/config/sc_lint.m4
@@ -0,0 +1,90 @@
+dnl
+dnl Copyright 2005-2006 Sun Microsystems, Inc.  All rights reserved.
+dnl
+dnl Permission is hereby granted, free of charge, to any person obtaining a
+dnl copy of this software and associated documentation files (the
+dnl "Software"), to deal in the Software without restriction, including
+dnl without limitation the rights to use, copy, modify, merge, publish,
+dnl distribute, and/or sell copies of the Software, and to permit persons
+dnl to whom the Software is furnished to do so, provided that the above
+dnl copyright notice(s) and this permission notice appear in all copies of
+dnl the Software and that both the above copyright notice(s) and this
+dnl permission notice appear in supporting documentation.
+dnl
+dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+dnl OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+dnl MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+dnl OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+dnl HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
+dnl INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
+dnl FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+dnl NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+dnl WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+dnl
+dnl Except as contained in this notice, the name of a copyright holder
+dnl shall not be used in advertising or otherwise to promote the sale, use
+dnl or other dealings in this Software without prior written authorization
+dnl of the copyright holder.
+dnl
+dnl Renamed by Carsten Burstedde <carsten at ices.utexas.edu>
+
+# SC_PROG_LINT()
+# ----------------
+# Minimum version: 1.1.0
+#
+# Sets up flags for source checkers such as lint and sparse if --with-lint
+# is specified.   (Use --with-lint=sparse for sparse.)
+# Sets $LINT to name of source checker passed with --with-lint (default: splint)
+# Sets $LINT_FLAGS to flags to pass to source checker
+# Sets LINT automake conditional if enabled (default: disabled)
+#
+# Note that MPI_INCLUDE_PATH should be defined before this function is called.
+#
+AC_DEFUN([SC_PROG_LINT],[
+
+# Allow checking code with lint, sparse, etc.
+AC_ARG_WITH([lint], [AS_HELP_STRING([--with-lint],
+            [use static source code checker (default: splint)])],
+            [use_lint=$withval], [use_lint=yes])
+if test "$use_lint" = yes ; then
+  use_lint="splint"
+fi
+if test "$use_lint" != no ; then
+  AC_PATH_PROG([LINT], [$use_lint], [no])
+  if test "$LINT" = no ; then
+    AC_MSG_WARN([Static source code checker $use_lint not found])
+    use_lint="no"
+  fi
+fi
+
+if test "$use_lint" != no ; then
+
+if test "$LINT_FLAGS" = "" ; then
+    case $LINT in
+      lint|*/lint)
+        case $host_os in
+          solaris*)
+            LINT_FLAGS="-u -b -h -erroff=E_INDISTING_FROM_TRUNC2"
+            ;;
+        esac
+        ;;
+      splint|*/splint)
+        LINT_FLAGS="-weak -fixedformalarray -badflag -preproc -unixlib"
+        ;;
+    esac
+fi
+
+case $LINT in
+  splint|*/splint)
+    LINT_FLAGS="$LINT_FLAGS -DSC_SPLINT \
+                -systemdirs /usr/include:$MPI_INCLUDE_PATH"
+    ;;
+esac
+
+fi
+
+AC_SUBST(LINT)
+AC_SUBST(LINT_FLAGS)
+AM_CONDITIONAL(LINT, [test "$use_lint" != no])
+
+])
diff --git a/sc/config/sc_mpi.m4 b/sc/config/sc_mpi.m4
new file mode 100644
index 0000000..4e66e36
--- /dev/null
+++ b/sc/config/sc_mpi.m4
@@ -0,0 +1,400 @@
+dnl
+dnl SC_MPI_CONFIG(PREFIX, , )
+dnl
+dnl If the second argument is nonempty, also includes configuration for F77 and FC.
+dnl If the third argument is nonempty, also includes configuration for CXX.
+dnl
+dnl Checks the configure options
+dnl --enable-mpi      If enabled and CC is not set, do export CC=mpicc.
+dnl                   This may be "too late" if AC_PROG_CC was called earlier.
+dnl                   In that case you need to set CC=mpicc (or other compiler)
+dnl                   on the configure command line.
+dnl                   Likewise for F77, FC and CXX if enabled in SC_MPI_CONFIG.
+dnl --disable-mpiio   Only effective if --enable-mpi is given.  In this case,
+dnl                   do not use MPI I/O in sc and skip the compile-and-link test.
+dnl --disable-mpithread Only effective if --enable-mpi is given.  In this case,
+dnl                   do not use MPI_Init_thread () and skip compile-and-link test.
+dnl
+dnl If MPI is enabled, set AC_DEFINE and AC_CONDITIONAL for PREFIX_ENABLE_MPI.
+dnl If MPI I/O is not disabled, set these for PREFIX_ENABLE_MPIIO.
+dnl If MPI_Init_thread is not disabled, set these for PREFIX_ENABLE_MPITHREAD.
+dnl
+dnl SC_MPI_ENGAGE(PREFIX)
+dnl
+dnl Relies on SC_MPI_CONFIG to be called before.
+dnl Calls AC_PROG_CC and other macros related to the C compiler.
+dnl Calls AC_PROG_F77 and others if F77 is enabled in SC_MPI_CONFIG.
+dnl Calls AC_PROG_FC and others if FC is enabled in SC_MPI_CONFIG.
+dnl Calls AC_PROG_CXX and others if CXX is enabled in SC_MPI_CONFIG.
+dnl
+dnl If MPI is enabled, a compile-and-link test is performed.  It aborts
+dnl configuration on failure.
+dnl If MPI is enabled and I/O is not disabled, a compile-and-link test
+dnl for MPI I/O is performed.  It aborts configuration on failure.
+dnl If MPI is enabled and MPITHREAD is not disabled, a compile-and-link test
+dnl for MPI_Init_thread is performed.  It aborts configuration on failure.
+dnl
+dnl These macros are separate because of the AC_REQUIRE logic inside autoconf.
+
+AC_DEFUN([SC_MPI_CONFIG],
+[
+HAVE_PKG_MPI=no
+HAVE_PKG_MPIIO=no
+HAVE_PKG_MPITHREAD=no
+m4_ifval([$2], [m4_define([SC_CHECK_MPI_F77], [yes])])
+m4_ifval([$2], [m4_define([SC_CHECK_MPI_FC], [yes])])
+m4_ifval([$3], [m4_define([SC_CHECK_MPI_CXX], [yes])])
+
+dnl The shell variable SC_ENABLE_MPI is set if --enable-mpi is given.
+dnl Therefore all further checking uses the HAVE_PKG_MPI shell variable
+dnl and neither AC_DEFINE nor AM_CONDITIONAL are invoked at this point.
+AC_ARG_ENABLE([mpi],
+              [AS_HELP_STRING([--enable-mpi],
+               [enable MPI (force serial code otherwise)])],,
+              [enableval=no])
+if test "x$enableval" = xyes ; then
+  HAVE_PKG_MPI=yes
+elif test "x$enableval" != xno ; then
+  AC_MSG_ERROR([Please use --enable-mpi without an argument])
+fi
+AC_MSG_CHECKING([whether we are using MPI])
+AC_MSG_RESULT([$HAVE_PKG_MPI])
+
+dnl The shell variable SC_ENABLE_MPIIO is set if --disable-mpiio is not given.
+dnl If not disabled, MPI I/O will be verified by a compile/link test below.
+AC_ARG_ENABLE([mpiio],
+              [AS_HELP_STRING([--disable-mpiio],
+               [do not use MPI I/O (even if MPI is enabled)])],,
+              [enableval=yes])
+if test "x$enableval" = xyes ; then
+  if test "x$HAVE_PKG_MPI" = xyes ; then
+    HAVE_PKG_MPIIO=yes
+  fi
+elif test "x$enableval" != xno ; then
+  AC_MSG_ERROR([Please don't use --enable-mpiio; it's the default now])
+fi
+AC_MSG_CHECKING([whether we are using MPI I/O])
+AC_MSG_RESULT([$HAVE_PKG_MPIIO])
+
+dnl The variable SC_ENABLE_MPITHREAD is set if --disable-mpithread not given.
+dnl If not disabled, MPI_Init_thread will be verified by a compile/link test.
+AC_ARG_ENABLE([mpithread],
+              [AS_HELP_STRING([--disable-mpithread],
+               [do not use MPI_Init_thread (even if MPI is enabled)])],,
+              [enableval=yes])
+if test "x$enableval" = xyes ; then
+  if test "x$HAVE_PKG_MPI" = xyes ; then
+    HAVE_PKG_MPITHREAD=yes
+  fi
+elif test "x$enableval" != xno ; then
+  AC_MSG_ERROR([Please don't use --enable-mpithread; it's the default now])
+fi
+AC_MSG_CHECKING([whether we are using MPI_Init_thread])
+AC_MSG_RESULT([$HAVE_PKG_MPITHREAD])
+
+dnl Establish the MPI test environment
+$1_MPIRUN=
+$1_MPI_TEST_FLAGS=
+if test "x$HAVE_PKG_MPI" = xyes ; then
+AC_CHECK_PROGS([$1_MPIRUN], [mpiexec mpirun])
+if test "x$$1_MPIRUN" = xmpiexec ; then
+  # $1_MPIRUN=mpiexec
+  $1_MPI_TEST_FLAGS="-n 2"
+elif test "x$$1_MPIRUN" = xmpirun ; then
+  # $1_MPIRUN=mpirun
+  $1_MPI_TEST_FLAGS="-np 2"
+else
+  $1_MPIRUN=
+fi
+AC_SUBST([$1_MPIRUN])
+AC_SUBST([$1_MPI_TEST_FLAGS])
+fi
+AM_CONDITIONAL([$1_MPIRUN], [test "x$$1_MPIRUN" != x])
+
+dnl Set compilers if not already set and set define and conditionals
+if test "x$HAVE_PKG_MPI" = xyes ; then
+m4_ifset([SC_CHECK_MPI_F77], [
+  if test "x$F77" = x ; then
+    export F77=mpif77
+  fi
+  AC_MSG_NOTICE([                            F77 set to $F77])
+])
+m4_ifset([SC_CHECK_MPI_FC], [
+  if test "x$FC" = x ; then
+    export FC=mpif90
+  fi
+  AC_MSG_NOTICE([                             FC set to $FC])
+])
+  if test "x$CC" = x ; then
+    export CC=mpicc
+  fi
+  AC_MSG_NOTICE([                             CC set to $CC])
+m4_ifset([SC_CHECK_MPI_CXX], [
+  if test "x$CXX" = x ; then
+    export CXX=mpicxx
+  fi
+  AC_MSG_NOTICE([                            CXX set to $CXX])
+])
+  AC_DEFINE([MPI], 1, [DEPRECATED (use $1_ENABLE_MPI instead)])
+  AC_DEFINE([ENABLE_MPI], 1, [Define to 1 if we are using MPI])
+  if test "x$HAVE_PKG_MPIIO" = xyes ; then
+    AC_DEFINE([MPIIO], 1, [DEPRECATED (use $1_ENABLE_MPIIO instead)])
+    AC_DEFINE([ENABLE_MPIIO], 1, [Define to 1 if we are using MPI I/O])
+  fi
+  if test "x$HAVE_PKG_MPITHREAD" = xyes ; then
+    AC_DEFINE([ENABLE_MPITHREAD], 1, [Define to 1 if we are using MPI_Init_thread])
+  fi
+else
+m4_ifset([SC_CHECK_MPI_F77], [
+  if test "x$F77" = x ; then
+    AC_CHECK_PROGS([$1_F77_COMPILER], [gfortran g77 f77 ifort])
+    if test "x$$1_F77_COMPILER" != x ; then
+      F77="$$1_F77_COMPILER"
+    fi
+  fi
+], [:])
+m4_ifset([SC_CHECK_MPI_FC], [
+  if test "x$FC" = x ; then
+    AC_CHECK_PROGS([$1_FC_COMPILER], [gfortran ifort])
+    if test "x$$1_FC_COMPILER" != x ; then
+      FC="$$1_FC_COMPILER"
+    fi
+  fi
+], [:])
+fi
+AM_CONDITIONAL([$1_ENABLE_MPI], [test "x$HAVE_PKG_MPI" = xyes])
+AM_CONDITIONAL([$1_ENABLE_MPIIO], [test "x$HAVE_PKG_MPIIO" = xyes])
+AM_CONDITIONAL([$1_ENABLE_MPITHREAD], [test "x$HAVE_PKG_MPITHREAD" = xyes])
+])
+
+dnl SC_MPI_F77_COMPILE_AND_LINK([action-if-successful], [action-if-failed])
+dnl Compile and link an MPI F77 test program
+dnl
+dnl DEACTIVATED since it triggers a bug in autoconf:
+dnl AC_LANG_PROGRAM(Fortran 77): ignoring PROLOGUE: [
+dnl
+dnl AC_DEFUN([SC_MPI_F77_COMPILE_AND_LINK],
+dnl [
+dnl AC_MSG_CHECKING([compile/link for MPI F77 program])
+dnl AC_LINK_IFELSE([AC_LANG_PROGRAM(
+dnl [[
+dnl       include "mpif.h"
+dnl ]], [[
+dnl       call MPI_INIT (ierror)
+dnl       call MPI_COMM_SIZE (MPI_COMM_WORLD, isize, ierror)
+dnl       call MPI_COMM_RANK (MPI_COMM_WORLD, irank, ierror)
+dnl       print*, isize, irank, ': Hello world'
+dnl       call MPI_FINALIZE (ierror)
+dnl ]])],
+dnl [AC_MSG_RESULT([successful])
+dnl  $1],
+dnl [AC_MSG_RESULT([failed])
+dnl  $2])
+dnl ])
+
+dnl SC_MPI_FC_COMPILE_AND_LINK([action-if-successful], [action-if-failed])
+dnl Compile and link an MPI FC test program
+dnl
+dnl DEACTIVATED since it triggers a bug in autoconf:
+dnl AC_LANG_PROGRAM(Fortran): ignoring PROLOGUE: [
+dnl
+dnl AC_DEFUN([SC_MPI_FC_COMPILE_AND_LINK],
+dnl [
+dnl AC_MSG_CHECKING([compile/link for MPI FC program])
+dnl AC_LINK_IFELSE([AC_LANG_PROGRAM(
+dnl [[
+dnl       include "mpif90.h"
+dnl ]], [[
+dnl       call MPI_INIT (ierror)
+dnl       call MPI_COMM_SIZE (MPI_COMM_WORLD, isize, ierror)
+dnl       call MPI_COMM_RANK (MPI_COMM_WORLD, irank, ierror)
+dnl       print*, isize, irank, ': Hello world'
+dnl       call MPI_FINALIZE (ierror)
+dnl ]])],
+dnl [AC_MSG_RESULT([successful])
+dnl  $1],
+dnl [AC_MSG_RESULT([failed])
+dnl  $2])
+dnl ])
+
+dnl SC_MPI_C_COMPILE_AND_LINK([action-if-successful], [action-if-failed])
+dnl Compile and link an MPI C test program
+dnl
+AC_DEFUN([SC_MPI_C_COMPILE_AND_LINK],
+[
+AC_MSG_CHECKING([compile/link for MPI C program])
+AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[
+#undef MPI
+#include <mpi.h>
+]], [[
+MPI_Init ((int *) 0, (char ***) 0);
+MPI_Finalize ();
+]])],
+[AC_MSG_RESULT([successful])
+ $1],
+[AC_MSG_RESULT([failed])
+ $2])
+])
+
+dnl SC_MPI_CXX_COMPILE_AND_LINK([action-if-successful], [action-if-failed])
+dnl Compile and link an MPI CXX test program
+dnl
+AC_DEFUN([SC_MPI_CXX_COMPILE_AND_LINK],
+[
+AC_MSG_CHECKING([compile/link for MPI CXX program])
+AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[
+#undef MPI
+#include <mpi.h>
+#include <iostream>
+]], [[
+std::cout << "Hello C++ MPI" << std::endl;
+MPI_Init ((int *) 0, (char ***) 0);
+MPI_Finalize ();
+]])],
+[AC_MSG_RESULT([successful])
+ $1],
+[AC_MSG_RESULT([failed])
+ $2])
+])
+
+dnl SC_MPIIO_C_COMPILE_AND_LINK([action-if-successful], [action-if-failed])
+dnl Compile and link an MPI I/O test program
+dnl
+AC_DEFUN([SC_MPIIO_C_COMPILE_AND_LINK],
+[
+AC_MSG_CHECKING([compile/link for MPI I/O C program])
+AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[
+#undef MPI
+#include <mpi.h>
+]], [[
+MPI_File fh;
+MPI_Init ((int *) 0, (char ***) 0);
+MPI_File_open (MPI_COMM_WORLD, "filename",
+               MPI_MODE_WRONLY | MPI_MODE_APPEND,
+               MPI_INFO_NULL, &fh);
+MPI_File_close (&fh);
+MPI_Finalize ();
+]])],
+[AC_MSG_RESULT([successful])
+ $1],
+[AC_MSG_RESULT([failed])
+ $2])
+])
+
+dnl SC_MPITHREAD_C_COMPILE_AND_LINK([action-if-successful], [action-if-failed])
+dnl Compile and link an MPI_Init_thread test program
+dnl
+AC_DEFUN([SC_MPITHREAD_C_COMPILE_AND_LINK],
+[
+AC_MSG_CHECKING([compile/link for MPI_Init_thread C program])
+AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[
+#undef MPI
+#include <mpi.h>
+]], [[
+int mpiret;
+int mpithr;
+mpiret = MPI_Init_thread ((int *) 0, (char ***) 0,
+                          MPI_THREAD_MULTIPLE, &mpithr);
+mpiret = MPI_Finalize ();
+]])],
+[AC_MSG_RESULT([successful])
+ $1],
+[AC_MSG_RESULT([failed])
+ $2])
+])
+
+dnl SC_MPI_INCLUDES
+dnl Call the compiler with various --show* options
+dnl to figure out the MPI_INCLUDES and MPI_INCLUDE_PATH varables
+dnl
+AC_DEFUN([SC_MPI_INCLUDES],
+[
+MPI_INCLUDES=
+MPI_INCLUDE_PATH=
+if test "x$HAVE_PKG_MPI" = xyes ; then
+  AC_MSG_NOTICE([Trying to determine MPI_INCLUDES])
+  for SHOW in -showme:compile -showme:incdirs -showme -show ; do
+    if test "x$MPI_INCLUDES" = x ; then
+      AC_MSG_CHECKING([$SHOW])
+      if MPI_CC_RESULT=`$CC $SHOW 2> /dev/null` ; then
+        AC_MSG_RESULT([Successful])
+        for CCARG in $MPI_CC_RESULT ; do
+          MPI_INCLUDES="$MPI_INCLUDES `echo $CCARG | grep '^-I'`"
+        done
+      else
+        AC_MSG_RESULT([Failed])
+      fi
+    fi
+  done
+  if test "x$MPI_INCLUDES" != x; then
+    MPI_INCLUDES=`echo $MPI_INCLUDES | sed -e 's/^ *//' -e 's/  */ /g'`
+    AC_MSG_NOTICE([   Found MPI_INCLUDES $MPI_INCLUDES])
+  fi
+  if test "x$MPI_INCLUDES" != x ; then
+    MPI_INCLUDE_PATH=`echo $MPI_INCLUDES | sed -e 's/^-I//'`
+    MPI_INCLUDE_PATH=`echo $MPI_INCLUDE_PATH | sed -e 's/-I/:/g'`
+    AC_MSG_NOTICE([   Found MPI_INCLUDE_PATH $MPI_INCLUDE_PATH])
+  fi
+fi
+AC_SUBST([MPI_INCLUDES])
+AC_SUBST([MPI_INCLUDE_PATH])
+])
+
+AC_DEFUN([SC_MPI_ENGAGE],
+[
+dnl determine compilers
+m4_ifset([SC_CHECK_MPI_F77], [
+AC_REQUIRE([AC_PROG_F77])
+AC_PROG_F77_C_O
+AC_REQUIRE([AC_F77_LIBRARY_LDFLAGS])
+AC_F77_WRAPPERS
+])
+m4_ifset([SC_CHECK_MPI_FC], [
+AC_REQUIRE([AC_PROG_FC])
+AC_PROG_FC_C_O
+AC_REQUIRE([AC_FC_LIBRARY_LDFLAGS])
+AC_FC_WRAPPERS
+])
+AC_REQUIRE([AC_PROG_CC])
+AC_PROG_CC_C_O
+AM_PROG_CC_C_O
+m4_ifset([SC_CHECK_MPI_CXX], [
+AC_REQUIRE([AC_PROG_CXX])
+AC_PROG_CXX_C_O
+])
+
+dnl compile and link tests must be done after the AC_PROC_CC lines
+if test "x$HAVE_PKG_MPI" = xyes ; then
+dnl  m4_ifset([SC_CHECK_MPI_F77], [
+dnl    AC_LANG_PUSH([Fortran 77])
+dnl    SC_MPI_F77_COMPILE_AND_LINK(, [AC_MSG_ERROR([MPI F77 test failed])])
+dnl    AC_LANG_POP([Fortran 77])
+dnl  ])
+dnl  m4_ifset([SC_CHECK_MPI_FC], [
+dnl    AC_LANG_PUSH([Fortran])
+dnl    SC_MPI_FC_COMPILE_AND_LINK(, [AC_MSG_ERROR([MPI FC test failed])])
+dnl    AC_LANG_POP([Fortran])
+dnl  ])
+  SC_MPI_C_COMPILE_AND_LINK(, [AC_MSG_ERROR([MPI C test failed])])
+  m4_ifset([SC_CHECK_MPI_CXX], [
+    AC_LANG_PUSH([C++])
+    SC_MPI_CXX_COMPILE_AND_LINK(, [AC_MSG_ERROR([MPI CXX test failed])])
+    AC_LANG_POP([C++])
+  ])
+  if test "x$HAVE_PKG_MPIIO" = xyes ; then
+    SC_MPIIO_C_COMPILE_AND_LINK(,
+      [AC_MSG_ERROR([MPI I/O not found; you may try --disable-mpiio])])
+  fi
+  if test "x$HAVE_PKG_MPITHREAD" = xyes ; then
+    SC_MPITHREAD_C_COMPILE_AND_LINK(,
+      [AC_MSG_ERROR([MPI_Init_thread not found; you may try --disable-mpithread])])
+  fi
+fi
+
+dnl figure out the MPI include directories
+SC_MPI_INCLUDES
+])
diff --git a/sc/config/sc_package.m4 b/sc/config/sc_package.m4
new file mode 100644
index 0000000..e79b060
--- /dev/null
+++ b/sc/config/sc_package.m4
@@ -0,0 +1,149 @@
+
+dnl sc_package.m4 - general custom macros
+dnl
+dnl This file is part of the SC Library.
+dnl The SC library provides support for parallel scientific applications.
+dnl
+dnl Copyright (C) 2008,2009,2014 Carsten Burstedde, Lucas C. Wilcox.
+
+dnl See SC_AS_SUBPACKAGE below for documentation of the mechanism
+
+dnl Documentation for macro names: brackets indicate optional arguments
+
+dnl SC_CHECK_INSTALL(PREFIX, REQUIRE_INCLUDE, REQUIRE_LDADD,
+dnl                  REQUIRE_CONFIG, REQUIRE_ETC)
+dnl The REQUIRE_* arguments can be either "true" or "false" (without quotes).
+dnl This function throws an error if the variable PREFIX_DIR does not exist.
+dnl The package must have been make install'd in that directory.
+dnl Optionally require include, lib, config, and etc subdirectories.
+dnl Set the shell variable PREFIX_INSTALL to "yes."
+dnl
+AC_DEFUN([SC_CHECK_INSTALL],
+[
+if test "x$$1_DIR" = xyes ; then
+  AC_MSG_ERROR([Please provide an argument as in --with-PACKAGE=<directory>])
+fi
+if test ! -d "$$1_DIR" ; then
+  AC_MSG_ERROR([Directory "$$1_DIR" does not exist])
+fi
+$1_INSTALL=yes
+$1_INC="$$1_DIR/include"
+$1_LIB="$$1_DIR/lib"
+$1_CFG="$$1_DIR/share/aclocal"
+$1_ETC="$$1_DIR/etc"
+if $2 && test ! -d "$$1_INC" ; then
+  AC_MSG_ERROR([Specified installation path $$1_INC not found])
+fi
+if $3 && test ! -d "$$1_LIB" ; then
+  AC_MSG_ERROR([Specified installation path $$1_LIB not found])
+fi
+if $4 && test ! -d "$$1_CFG" ; then
+  AC_MSG_ERROR([Specified installation path $$1_CFG not found])
+fi
+if $5 && test ! -d "$$1_ETC" ; then
+  AC_MSG_ERROR([Specified installation path $$1_ETC not found])
+fi
+])
+
+dnl SC_CHECK_PACKAGE(PREFIX, REQUIRE_INCLUDE, REQUIRE_LDADD,
+dnl                  REQUIRE_CONFIG, REQUIRE_ETC)
+dnl The REQUIRE_* arguments can be either "true" or "false" (without quotes).
+dnl This function throws an error if the variable PREFIX_DIR does not exist.
+dnl Looks for PREFIX_DIR/src to identify a source distribution.
+dnl If not found, package must have been `make install`ed, in this case
+dnl optionally require include, lib, config, and etc directories.
+dnl Set the shell variable PREFIX_INSTALL to "yes" or "no."
+dnl
+AC_DEFUN([SC_CHECK_PACKAGE],
+[
+if test ! -d "$$1_DIR" ; then
+  AC_MSG_ERROR([Directory "$$1_DIR" does not exist])
+fi
+if test -d "$$1_DIR/src" ; then
+  $1_INSTALL=no
+  $1_INC="$$1_DIR/src"
+  $1_LIB="$$1_DIR/src"
+  $1_CFG="$$1_DIR/config"
+  $1_ETC=
+  if $4 && test ! -d "$$1_CFG" ; then
+    AC_MSG_ERROR([Specified source path $$1_CFG not found])
+  fi
+else
+  SC_CHECK_INSTALL([$1], [$2], [$3], [$4], [$5])
+fi
+])
+
+dnl---------------------- HOW SUBPACKAGES WORK ---------------------------
+dnl
+dnl A program PROG relies on libsc (or another package called ME, which
+dnl could itself be using libsc).  In a build from source, me usually
+dnl resides in PROG's subdirectory me.  This location can be overridden by
+dnl the environment variable PROG_ME_SOURCE; this situation can arise when
+dnl both PROG and me are subpackages to yet another software.  The
+dnl path in PROG_ME_SOURCE must be relative to PROG's toplevel directory.
+dnl All of this works without specifying a configure command line option.
+dnl However, if me is already make install'd in the system and should
+dnl be used from there, use --with-me=<path to me's install directory>.
+dnl In this case, PROG expects subdirectories etc, include, lib, and
+dnl share/aclocal, which are routinely created by me's make install.
+dnl
+dnl SC_ME_AS_SUBPACKAGE(PREFIX, prefix, ME, me)
+dnl Call from a package that is using this package ME as a subpackage.
+dnl Sets PREFIX_DIST_DENY=yes if me is make install'd.
+dnl
+AC_DEFUN([SC_ME_AS_SUBPACKAGE],
+[
+$1_$3_SUBDIR=
+$1_$3_MK_USE=
+$1_DISTCLEAN="$$1_DISTCLEAN $1_$3_SOURCE.log"
+
+SC_ARG_WITH_PREFIX([$4], [path to installed package $4 (optional)], [$3], [$1])
+
+if test "x$$1_WITH_$3" != xno ; then
+  AC_MSG_NOTICE([Using make installed package $4])
+
+  # Verify that we are using a me installation
+  $1_DIST_DENY=yes
+  $1_$3_DIR="$$1_WITH_$3"
+  SC_CHECK_INSTALL([$1_$3], [true], [true], [true], [true])
+
+  # Set variables for using the subpackage
+  $1_$3_AMFLAGS="-I $$1_$3_CFG"
+  $1_$3_MK_USE=yes
+  $1_$3_MK_INCLUDE="include $$1_$3_ETC/Makefile.$4.mk"
+  $1_$3_CPPFLAGS="\$($3_CPPFLAGS)"
+  $1_$3_LDADD="$$1_$3_DIR/lib/lib$4.la"
+else
+  AC_MSG_NOTICE([Building with source of package $4])
+
+  # Prepare for a build using me sources
+  if test "x$$1_$3_SOURCE" = x ; then
+    if test -f "$1_$3_SOURCE.log" ; then
+      $1_$3_SOURCE=`cat $1_$3_SOURCE.log`
+    else
+      $1_$3_SOURCE="$4"
+      $1_$3_SUBDIR="$4"
+      AC_CONFIG_SUBDIRS([$4])
+    fi
+  else
+    AC_CONFIG_COMMANDS([$1_$3_SOURCE.log],
+                       [echo "$$1_$3_SOURCE" >$1_$3_SOURCE.log])
+  fi
+  $1_$3_AMFLAGS="-I \$(top_srcdir)/$$1_$3_SOURCE/config"
+  $1_$3_MK_INCLUDE="include \${$2_sysconfdir}/Makefile.$4.mk"
+  $1_$3_CPPFLAGS="-I$$1_$3_SOURCE/src -I\$(top_srcdir)/$$1_$3_SOURCE/src"
+  $1_$3_LDADD="$$1_$3_SOURCE/src/lib$4.la"
+fi
+
+dnl Make sure we find the m4 macros provided by me
+AC_SUBST([$1_$3_AMFLAGS])
+
+dnl We call make in this subdirectory if not empty
+AC_SUBST([$1_$3_SUBDIR])
+
+dnl We will need these variables to compile and link with me
+AM_CONDITIONAL([$1_$3_MK_USE], [test "x$$1_$3_MK_USE" != x])
+AC_SUBST([$1_$3_MK_INCLUDE])
+AC_SUBST([$1_$3_CPPFLAGS])
+AC_SUBST([$1_$3_LDADD])
+])
diff --git a/sc/config/sc_pthread.m4 b/sc/config/sc_pthread.m4
new file mode 100644
index 0000000..165a08a
--- /dev/null
+++ b/sc/config/sc_pthread.m4
@@ -0,0 +1,42 @@
+
+dnl SC_CHECK_PTHREAD(PREFIX)
+dnl Check for POSIX thread support and link a test program
+dnl
+AC_DEFUN([SC_CHECK_PTHREAD], [
+
+SC_CHECK_LIB([pthread], [pthread_create], [LPTHREAD], [$1])
+AC_MSG_CHECKING([for POSIX threads])
+
+SC_ARG_ENABLE_PREFIX([pthread],
+  [enable POSIX threads (optionally use --enable-pthread=<PTHREAD_CFLAGS>)],
+  [PTHREAD], [$1])
+if test "x$$1_ENABLE_PTHREAD" != xno ; then
+  $1_PTHREAD_CFLAGS=
+  if test "x$$1_ENABLE_PTHREAD" != xyes ; then
+    $1_PTHREAD_CFLAGS="$$1_ENABLE_PTHREAD"
+    dnl AC_MSG_ERROR([Please provide --enable-pthread without arguments])
+  fi
+  PRE_PTHREAD_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS $$1_PTHREAD_CFLAGS"
+  AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[
+#include <pthread.h>
+void *start_routine (void *v)
+{
+  return NULL;
+}
+]],[[
+  pthread_t thread;
+  pthread_create (&thread, NULL, &start_routine, NULL);
+  pthread_join (thread, NULL);
+  pthread_exit (NULL);
+]])],,
+                 [AC_MSG_ERROR([Unable to link with POSIX threads])])
+dnl Keep the variables changed as done above
+dnl CFLAGS="$PRE_PTHREAD_CFLAGS"
+
+  AC_MSG_RESULT([successful])
+else
+  AC_MSG_RESULT([not used])
+fi
+])
diff --git a/sc/config/sc_trilinos.m4 b/sc/config/sc_trilinos.m4
new file mode 100644
index 0000000..b986b7c
--- /dev/null
+++ b/sc/config/sc_trilinos.m4
@@ -0,0 +1,122 @@
+
+dnl trilinos.m4 - general custom macros
+dnl
+dnl This file is part of the SC Library.
+dnl The SC library provides support for parallel scientific applications.
+dnl
+dnl Copyright (C) 2008-2010 Carsten Burstedde, Lucas Wilcox.
+
+dnl Documentation for macro names: brackets indicate optional arguments
+
+dnl SC_TRILINOS_CHECK_MK(package, Package, PACKAGE, PREFIX)
+dnl Check for the Makefile of a trilinos package
+dnl Requires variable SC_TRILINOS_DIR pointing to a trilinos installation
+dnl
+AC_DEFUN([SC_TRILINOS_CHECK_MK],
+[
+dnl Trilinos <= 9
+$4_TRILINOS_MK_$3="$$4_TRILINOS_DIR/include/Makefile.export.$1"
+if test ! -f "$$4_TRILINOS_MK_$3" ; then
+  dnl Trilinos 10
+  $4_TRILINOS_MK_$3="$$4_TRILINOS_DIR/include/Makefile.export.$2"
+  if test ! -f "$$4_TRILINOS_MK_$3" ; then
+    AC_MSG_ERROR([$$4_TRILINOS_MK_$3 not found])
+  fi
+fi
+AC_SUBST([$4_TRILINOS_MK_$3])
+])
+
+dnl SC_TRILINOS_PACKAGE_DEFS([Package], [PACKAGE], [PREFIX])
+dnl define PREFIX_PACKAGE_{CPPFLAGS,LDFLAGS,LIBS} for use with Trilinos export
+dnl Makefiles
+AC_DEFUN([SC_TRILINOS_PACKAGE_DEFS],
+[
+dnl for Trilinos 9, use PACKAGE_{INCLUDES,LIBS}
+  if test "$$3_TRILINOS_VERSION" = "9" ; then
+    $3_$2_CPPFLAGS="\$($2_INCLUDES)"
+    $3_$2_LDFLAGS=""
+    $3_$2_LIBS="\$($2_LIBS)"
+  else
+    AC_MSG_NOTICE([TRILINOS_MINOR_VERSION $$3_TRILINOS_MINOR_VERSION])
+    case "$$3_TRILINOS_MINOR_VERSION" in
+dnl 0 and 2 are the only official releases with all-caps
+    0[[0-2]])
+      $3_$2_CPPFLAGS="\$($2_INCLUDE_DIRS) \$($2_TPL_INCLUDE_DIRS)"
+      $3_$2_LDFLAGS="\$($2_SHARED_LIB_RPATH_COMMAND) \$($2_EXTRA_LD_FLAGS) "\
+"\$($2_LIBRARY_DIRS) \$($2_TPL_LIBRARY_DIRS)"
+      $3_$2_LIBS="\$($2_LIBRARIES)"
+      ;;
+    *)
+      $3_$2_CPPFLAGS="\$($1_INCLUDE_DIRS) \$($1_TPL_INCLUDE_DIRS)"
+      $3_$2_LDFLAGS="\$($1_SHARED_LIB_RPATH_COMMAND) \$($1_EXTRA_LD_FLAGS) "\
+"\$($1_LIBRARY_DIRS) \$($1_TPL_LIBRARY_DIRS)"
+      $3_$2_LIBS="\$($1_LIBRARIES)"
+      ;;
+    esac
+  fi
+  AC_SUBST([$3_$2_CPPFLAGS])
+  AC_SUBST([$3_$2_LDFLAGS])
+  AC_SUBST([$3_$2_LIBS])
+])
+
+dnl SC_TRILINOS([PREFIX], [EXTRA_PACKAGES])
+dnl EXTRA_PACKAGES can be empty or contain a comma-separated list
+dnl of trilinos packages in uppercase.
+dnl Currently only ML is recognized.
+dnl
+AC_DEFUN([SC_TRILINOS],
+[
+$1_TRILINOS_VERSION=
+SC_ARG_WITH_PREFIX([trilinos], [set <dir> to Trilinos installation],
+                   [TRILINOS], [$1], [=<dir>])
+if test "$$1_WITH_TRILINOS" != "no" ; then
+  if test "$$1_WITH_TRILINOS" = "yes" ; then
+    AC_MSG_ERROR([Please specify Trilinos installation --with-trilinos=<dir>])
+  else
+    AC_MSG_CHECKING([Trilinos include directory and Makefiles])
+    $1_TRILINOS_DIR="$$1_WITH_TRILINOS"
+    if test ! -d "$$1_TRILINOS_DIR" ; then
+      AC_MSG_ERROR([$$1_TRILINOS_DIR not found])
+    fi
+    if test ! -d "$$1_TRILINOS_DIR/include" ; then
+      AC_MSG_ERROR([$$1_TRILINOS_DIR/include not found])
+    fi
+    if test ! -d "$$1_TRILINOS_DIR/lib" ; then
+      AC_MSG_ERROR([$$1_TRILINOS_DIR/lib not found])
+    fi
+    TRILINOS_HEADER="$$1_TRILINOS_DIR/include/Trilinos_version.h"
+    if test ! -f "$TRILINOS_HEADER" ; then
+      AC_MSG_ERROR([Header file $TRILINOS_HEADER not found])
+    fi
+    if grep -qs 'TRILINOS_MAJOR_VERSION[[[:space:]+]]10' "$TRILINOS_HEADER"
+    then
+      $1_TRILINOS_VERSION=10
+      $1_TRILINOS_CPPFLAGS="-I$$1_TRILINOS_DIR/include"
+      AC_SUBST([$1_TRILINOS_CPPFLAGS])
+      $1_TRILINOS_LDFLAGS="-L$$1_TRILINOS_DIR/lib"
+      AC_SUBST([$1_TRILINOS_LDFLAGS])
+      $1_TRILINOS_MINOR_VERSION=`grep -o 'TRILINOS_MAJOR_MINOR_VERSION 10[[0-9]]\{2\}' "$TRILINOS_HEADER" | sed "s/.* 10//"`
+      AC_MSG_NOTICE([TRILINOS_MINOR_VERSION $$1_TRILINOS_MINOR_VERSION])
+    elif grep -qs 'TRILINOS_MAJOR_VERSION[[[:space:]+]]9' "$TRILINOS_HEADER"
+    then
+      $1_TRILINOS_VERSION=9
+    else
+      AC_MSG_ERROR([Trilinos version not recognized])
+    fi
+    SC_TRILINOS_CHECK_MK([epetra], [Epetra], [EPETRA], [$1])
+    SC_TRILINOS_CHECK_MK([teuchos], [Teuchos], [TEUCHOS], [$1])
+    SC_TRILINOS_PACKAGE_DEFS([Epetra], [EPETRA], [$1])
+    SC_TRILINOS_PACKAGE_DEFS([Teuchos], [TEUCHOS], [$1])
+    m4_foreach([PKG], [$2], [
+      if test "PKG" = "ML" ; then
+        SC_TRILINOS_CHECK_MK([ml], [ML], [ML], [$1])
+        SC_TRILINOS_PACKAGE_DEFS([ML], [ML], [$1])
+      fi
+    ])
+    AC_MSG_RESULT([version $$1_TRILINOS_VERSION])
+  fi
+fi
+AM_CONDITIONAL([$1_TRILINOS_9], [test "$$1_TRILINOS_VERSION" = 9])
+AM_CONDITIONAL([$1_TRILINOS_10], [test "$$1_TRILINOS_VERSION" = 10])
+AM_CONDITIONAL([$1_TRILINOS_ML], [test -n "$$1_TRILINOS_MK_ML"])
+])
diff --git a/sc/configure.ac b/sc/configure.ac
new file mode 100644
index 0000000..11c4f52
--- /dev/null
+++ b/sc/configure.ac
@@ -0,0 +1,132 @@
+dnl
+dnl This file is part of the SC Library.
+dnl
+
+AC_INIT([libsc],
+        [m4_esyscmd([build-aux/git-version-gen .tarball-version])],
+        [p4est at librelist.com])
+AC_PREREQ(2.61)
+AC_CONFIG_HEADERS([src/pre_config.h])
+AC_CONFIG_SRCDIR([src/sc.h])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CONFIG_MACRO_DIR([config])
+AC_PREFIX_DEFAULT([$PWD/local])
+AX_PREFIX_CONFIG_H([src/sc_config.h], [SC])
+AM_INIT_AUTOMAKE([parallel-tests subdir-objects])
+AM_SILENT_RULES
+SC_VERSION([SC])
+
+echo "o---------------------------------------"
+echo "| Checking options"
+echo "o---------------------------------------"
+
+AC_ARG_ENABLE([logging],
+              [AS_HELP_STRING([--enable-logging=PRIO], [\
+change log threshold (see sc.h for possible values)])],
+              [case "$enableval" in
+                 yes) AC_MSG_ERROR([\
+See sc.h for possible log priorities in --enable-logging=PRIO])
+                 ;;
+                 no) AC_DEFINE([LOG_PRIORITY], [SC_LP_SILENT],
+                               [minimal log priority])
+                 ;;
+                 *) AC_DEFINE_UNQUOTED([LOG_PRIORITY], [$enableval],
+                                       [minimal log priority])
+               esac])
+SC_ARG_ENABLE([debug], [enable debug mode (assertions and extra checks)],
+              [DEBUG])
+SC_ARG_WITH([papi], [enable Flop counting with papi], [PAPI])
+
+echo "o---------------------------------------"
+echo "| Checking MPI and related programs"
+echo "o---------------------------------------"
+
+dnl A nonempty second/third argument causes to enable F77+FC/CXX, respectively.
+SC_MPI_CONFIG([SC], [yes], [yes])
+SC_MPI_ENGAGE([SC])
+# This is needed for compatibility with automake >= 1.12
+m4_ifdef([AM_PROG_AR],[AM_PROG_AR])
+SC_PROG_LINT
+dnl SC_C_VERSION
+LT_INIT
+
+echo "o---------------------------------------"
+echo "| Checking libraries"
+echo "o---------------------------------------"
+
+SC_CHECK_LIBRARIES([SC])
+
+echo "o---------------------------------------"
+echo "| Checking headers"
+echo "o---------------------------------------"
+
+AC_CHECK_HEADERS([execinfo.h signal.h sys/time.h sys/types.h time.h])
+AC_CHECK_HEADERS([lua.h lua5.1/lua.h lua5.2/lua.h])
+
+echo "o---------------------------------------"
+echo "| Checking functions"
+echo "o---------------------------------------"
+
+AC_CHECK_FUNCS([backtrace backtrace_symbols strtol strtoll])
+
+echo "o---------------------------------------"
+echo "| Checking keywords and types"
+echo "o---------------------------------------"
+
+AC_C_BIGENDIAN([AC_DEFINE([IS_BIGENDIAN], 1, [Define to 1 on a bigendian machine])])
+AC_C_CONST
+AC_C_INLINE
+AC_C_RESTRICT
+AC_CHECK_SIZEOF([long])
+AC_CHECK_SIZEOF([long long])
+AC_CHECK_SIZEOF([unsigned long])
+AC_CHECK_SIZEOF([unsigned long long])
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+
+# Print summary.
+
+AC_DEFINE_UNQUOTED(F77,         ["${F77}"],         [F77 compiler])
+AC_DEFINE_UNQUOTED(FFLAGS,      ["${FFLAGS}"],      [F77 compiler flags])
+AC_DEFINE_UNQUOTED(FC,          ["${FC}"],          [FC compiler])
+AC_DEFINE_UNQUOTED(FCFLAGS,     ["${FCFLAGS}"],     [FC compiler flags])
+AC_DEFINE_UNQUOTED(CPP,         ["${CPP}"],         [C preprocessor])
+AC_DEFINE_UNQUOTED(CPPFLAGS,    ["${CPPFLAGS}"],    [C preprocessor flags])
+AC_DEFINE_UNQUOTED(CC,          ["${CC}"],          [C compiler])
+dnl AC_DEFINE_UNQUOTED(C_VERSION,   ["${C_VERSION}"],   [C compiler version])
+AC_DEFINE_UNQUOTED(CFLAGS,      ["${CFLAGS}"],      [C compiler flags])
+AC_DEFINE_UNQUOTED(CXX,         ["${CXX}"],         [CXX compiler])
+AC_DEFINE_UNQUOTED(CXXFLAGS,    ["${CXXFLAGS}"],    [CXX compiler flags])
+AC_DEFINE_UNQUOTED(LDFLAGS,     ["${LDFLAGS}"],     [Linker flags])
+AC_DEFINE_UNQUOTED(LIBS,        ["${LIBS}"],        [Libraries])
+dnl AC_DEFINE_UNQUOTED(BLAS_LIBS,   ["${BLAS_LIBS}"],   [BLAS libraries])
+dnl AC_DEFINE_UNQUOTED(LAPACK_LIBS, ["${LAPACK_LIBS}"], [LAPACK libraries])
+dnl AC_DEFINE_UNQUOTED(FLIBS,       ["${FLIBS}"],       [Fortran libraries])
+
+echo "o----------------------------------"
+echo "| Results for libsc are"
+echo "o----------------------------------"
+echo "| F77:         $F77"
+echo "| FFLAGS:      $FFLAGS"
+echo "| FC:          $FC"
+echo "| FCFLAGS:     $FCFLAGS"
+echo "| CPP:         $CPP"
+echo "| CPPFLAGS:    $CPPFLAGS"
+echo "| CC:          $CC"
+dnl echo "| C_VERSION:   $C_VERSION"
+echo "| CFLAGS:      $CFLAGS"
+echo "| CXX:         $CXX"
+echo "| CXXFLAGS:    $CXXFLAGS"
+echo "| LDFLAGS:     $LDFLAGS"
+echo "| LIBS:        $LIBS"
+dnl echo "| FLIBS:       $FLIBS"
+dnl echo "| BLAS_LIBS:   $BLAS_LIBS"
+dnl echo "| LAPACK_LIBS: $LAPACK_LIBS"
+echo "o----------------------------------"
+
+# Create output files.
+AC_CONFIG_FILES([Makefile Makefile.sc.pre Doxyfile])
+AC_OUTPUT
+
+# Final notifications.
+SC_FINAL_MESSAGES([SC])
diff --git a/sc/doc/author_seyboldt.txt b/sc/doc/author_seyboldt.txt
new file mode 100644
index 0000000..e745db0
--- /dev/null
+++ b/sc/doc/author_seyboldt.txt
@@ -0,0 +1 @@
+I hereby place my contributions to libsc into the public domain.  Adrian Seyboldt
\ No newline at end of file
diff --git a/sc/doc/author_weischer.txt b/sc/doc/author_weischer.txt
new file mode 100644
index 0000000..089ecda
--- /dev/null
+++ b/sc/doc/author_weischer.txt
@@ -0,0 +1 @@
+I hereby place my contributions to libsc into the public domain.  Sarah Weischer
diff --git a/sc/doc/coding_standards.txt b/sc/doc/coding_standards.txt
new file mode 100644
index 0000000..3eb733a
--- /dev/null
+++ b/sc/doc/coding_standards.txt
@@ -0,0 +1,32 @@
+
+This file describes a list of coding standards that the SC Library
+tries to adhere to.
+
+ * Indentation
+
+   All .c and .h files must be indented with the scindent script in
+   the main directory.  It calls GNU indent with some extra options.
+   This does not apply for included third party code.
+
+ * Boolean variables
+
+   All boolean parameters will not be checked with (a==true) or (a==false).
+   We follow the standard C convention where (a) is used to test true
+   and (!a) is used to test false.  The bool type is not used.
+   An exception is to test for pointers like (p == NULL) or (p != NULL).
+
+ * Variable names that are not self-descriptive
+
+   Loop variables of type int can be named as usual: i, j, etc.
+   Loop variables of type size_t should be named iz, jz, etc.
+   Loop variables of type ssize_t should be named is, js, etc.
+
+ * Printf and friends on 64bit integers, size_t, etc.
+
+   We will have to wait until all compilers and some lint checkers
+   understand printf ("%jd %ju", (intmax_t) i, (uintmax_t) u) since that
+   is a nice way to accomodate 128bit integers without changing the printf
+   code.  In the mean time, we're using printf ("%lld %lld", (long long)
+   i, (long long) u) which loses 128bit integers and the high bit of uint64_t.
+   This also holds for size_t since support for the following is still
+   incomplete: printf ("%zd %zu", ssize_t_var, size_t_var).
diff --git a/sc/doc/doxygen.css b/sc/doc/doxygen.css
new file mode 100644
index 0000000..23a5e80
--- /dev/null
+++ b/sc/doc/doxygen.css
@@ -0,0 +1,1164 @@
+/* The standard CSS for doxygen */
+
+body, table, div, p, dl {
+/*	font: 400 14px/19px Roboto,sans-serif; */
+	font: 400 14px/19px;
+}
+
+/* @group Heading Levels */
+
+h1 {
+	font-size: 150%;
+}
+
+.title {
+	font-size: 150%;
+	font-weight: bold;
+	margin: 10px 2px;
+}
+
+h2 {
+	border-bottom: 1px solid #87CBA9;
+	color: #357B58;
+	font-size: 150%;
+	font-weight: normal;
+	margin-top: 1.75em;
+	padding-top: 8px;
+	padding-bottom: 4px;
+	width: 100%;
+}
+
+h3 {
+	font-size: 100%;
+}
+
+h1, h2, h3, h4, h5, h6 {
+	-webkit-transition: text-shadow 0.5s linear;
+	-moz-transition: text-shadow 0.5s linear;
+	-ms-transition: text-shadow 0.5s linear;
+	-o-transition: text-shadow 0.5s linear;
+	transition: text-shadow 0.5s linear;
+	margin-right: 15px;
+}
+
+h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow {
+	text-shadow: 0 0 15px cyan;
+}
+
+dt {
+	font-weight: bold;
+}
+
+div.multicol {
+	-moz-column-gap: 1em;
+	-webkit-column-gap: 1em;
+	-moz-column-count: 3;
+	-webkit-column-count: 3;
+}
+
+p.startli, p.startdd, p.starttd {
+	margin-top: 2px;
+}
+
+p.endli {
+	margin-bottom: 0px;
+}
+
+p.enddd {
+	margin-bottom: 4px;
+}
+
+p.endtd {
+	margin-bottom: 2px;
+}
+
+/* @end */
+
+caption {
+	font-weight: bold;
+}
+
+span.legend {
+        font-size: 70%;
+        text-align: center;
+}
+
+h3.version {
+        font-size: 90%;
+        text-align: center;
+}
+
+div.qindex, div.navtab{
+	background-color: #EBF6F1;
+	border: 1px solid #A3D7BD;
+	text-align: center;
+}
+
+div.qindex, div.navpath {
+	width: 100%;
+	line-height: 140%;
+}
+
+div.navtab {
+	margin-right: 15px;
+}
+
+/* @group Link Styling */
+
+a {
+	color: #3D8C64;
+	font-weight: normal;
+	text-decoration: none;
+}
+
+.contents a:visited {
+	color: #46A274;
+}
+
+a:hover {
+	text-decoration: underline;
+}
+
+a.qindex {
+	font-weight: bold;
+}
+
+a.qindexHL {
+	font-weight: bold;
+	background-color: #9CD4B8;
+	color: #ffffff;
+	border: 1px double #86CAA8;
+}
+
+.contents a.qindexHL:visited {
+        color: #ffffff;
+}
+
+a.el {
+	font-weight: bold;
+}
+
+a.elRef {
+}
+
+a.code, a.code:visited {
+	color: #4665A2; 
+}
+
+a.codeRef, a.codeRef:visited {
+	color: #4665A2; 
+}
+
+/* @end */
+
+dl.el {
+	margin-left: -1cm;
+}
+
+pre.fragment {
+        border: 1px solid #C4CFE5;
+        background-color: #FBFCFD;
+        padding: 4px 6px;
+        margin: 4px 8px 4px 2px;
+        overflow: auto;
+        word-wrap: break-word;
+        font-size:  9pt;
+        line-height: 125%;
+        font-family: monospace, fixed;
+        font-size: 105%;
+}
+
+div.fragment {
+        padding: 4px;
+        margin: 4px;
+	background-color: #FBFDFC;
+	border: 1px solid #C4E5D5;
+}
+
+div.line {
+	font-family: monospace, fixed;
+        font-size: 13px;
+	min-height: 13px;
+	line-height: 1.0;
+	text-wrap: unrestricted;
+	white-space: -moz-pre-wrap; /* Moz */
+	white-space: -pre-wrap;     /* Opera 4-6 */
+	white-space: -o-pre-wrap;   /* Opera 7 */
+	white-space: pre-wrap;      /* CSS3  */
+	word-wrap: break-word;      /* IE 5.5+ */
+	text-indent: -53px;
+	padding-left: 53px;
+	padding-bottom: 0px;
+	margin: 0px;
+	-webkit-transition-property: background-color, box-shadow;
+	-webkit-transition-duration: 0.5s;
+	-moz-transition-property: background-color, box-shadow;
+	-moz-transition-duration: 0.5s;
+	-ms-transition-property: background-color, box-shadow;
+	-ms-transition-duration: 0.5s;
+	-o-transition-property: background-color, box-shadow;
+	-o-transition-duration: 0.5s;
+	transition-property: background-color, box-shadow;
+	transition-duration: 0.5s;
+}
+
+div.line.glow {
+	background-color: cyan;
+	box-shadow: 0 0 10px cyan;
+}
+
+
+span.lineno {
+	padding-right: 4px;
+	text-align: right;
+	border-right: 2px solid #0F0;
+	background-color: #E8E8E8;
+        white-space: pre;
+}
+span.lineno a {
+	background-color: #D8D8D8;
+}
+
+span.lineno a:hover {
+	background-color: #C8C8C8;
+}
+
+div.ah {
+	background-color: black;
+	font-weight: bold;
+	color: #ffffff;
+	margin-bottom: 3px;
+	margin-top: 3px;
+	padding: 0.2em;
+	border: solid thin #333;
+	border-radius: 0.5em;
+	-webkit-border-radius: .5em;
+	-moz-border-radius: .5em;
+	box-shadow: 2px 2px 3px #999;
+	-webkit-box-shadow: 2px 2px 3px #999;
+	-moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+	background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444));
+	background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000);
+}
+
+div.groupHeader {
+	margin-left: 16px;
+	margin-top: 12px;
+	font-weight: bold;
+}
+
+div.groupText {
+	margin-left: 16px;
+	font-style: italic;
+}
+
+body {
+	background-color: white;
+	color: black;
+        margin: 0;
+}
+
+div.contents {
+	margin-top: 10px;
+	margin-left: 12px;
+	margin-right: 8px;
+}
+
+td.indexkey {
+	background-color: #EBF6F1;
+	font-weight: bold;
+	border: 1px solid #C4E5D5;
+	margin: 2px 0px 2px 0;
+	padding: 2px 10px;
+        white-space: nowrap;
+        vertical-align: top;
+}
+
+td.indexvalue {
+	background-color: #EBF6F1;
+	border: 1px solid #C4E5D5;
+	padding: 2px 10px;
+	margin: 2px 0px;
+}
+
+tr.memlist {
+	background-color: #EEF7F2;
+}
+
+p.formulaDsp {
+	text-align: center;
+}
+
+img.formulaDsp {
+	
+}
+
+img.formulaInl {
+	vertical-align: middle;
+}
+
+div.center {
+	text-align: center;
+        margin-top: 0px;
+        margin-bottom: 0px;
+        padding: 0px;
+}
+
+div.center img {
+	border: 0px;
+}
+
+address.footer {
+	text-align: right;
+	padding-right: 12px;
+}
+
+img.footer {
+	border: 0px;
+	vertical-align: middle;
+}
+
+/* @group Code Colorization */
+
+span.keyword {
+	color: #008000
+}
+
+span.keywordtype {
+	color: #604020
+}
+
+span.keywordflow {
+	color: #e08000
+}
+
+span.comment {
+	color: #800000
+}
+
+span.preprocessor {
+	color: #806020
+}
+
+span.stringliteral {
+	color: #002080
+}
+
+span.charliteral {
+	color: #008080
+}
+
+span.vhdldigit { 
+	color: #ff00ff 
+}
+
+span.vhdlchar { 
+	color: #000000 
+}
+
+span.vhdlkeyword { 
+	color: #700070 
+}
+
+span.vhdllogic { 
+	color: #ff0000 
+}
+
+blockquote {
+        background-color: #F7FBF9;
+        border-left: 2px solid #9CD4B8;
+        margin: 0 24px 0 4px;
+        padding: 0 12px 0 16px;
+}
+
+/* @end */
+
+/*
+.search {
+	color: #003399;
+	font-weight: bold;
+}
+
+form.search {
+	margin-bottom: 0px;
+	margin-top: 0px;
+}
+
+input.search {
+	font-size: 75%;
+	color: #000080;
+	font-weight: normal;
+	background-color: #e8eef2;
+}
+*/
+
+td.tiny {
+	font-size: 75%;
+}
+
+.dirtab {
+	padding: 4px;
+	border-collapse: collapse;
+	border: 1px solid #A3D7BD;
+}
+
+th.dirtab {
+	background: #EBF6F1;
+	font-weight: bold;
+}
+
+hr {
+	height: 0px;
+	border: none;
+	border-top: 1px solid #4AAA7A;
+}
+
+hr.footer {
+	height: 1px;
+}
+
+/* @group Member Descriptions */
+
+table.memberdecls {
+	border-spacing: 0px;
+	padding: 0px;
+}
+
+.memberdecls td, .fieldtable tr {
+	-webkit-transition-property: background-color, box-shadow;
+	-webkit-transition-duration: 0.5s;
+	-moz-transition-property: background-color, box-shadow;
+	-moz-transition-duration: 0.5s;
+	-ms-transition-property: background-color, box-shadow;
+	-ms-transition-duration: 0.5s;
+	-o-transition-property: background-color, box-shadow;
+	-o-transition-duration: 0.5s;
+	transition-property: background-color, box-shadow;
+	transition-duration: 0.5s;
+}
+
+.memberdecls td.glow, .fieldtable tr.glow {
+	background-color: cyan;
+	box-shadow: 0 0 15px cyan;
+}
+
+.mdescLeft, .mdescRight,
+.memItemLeft, .memItemRight,
+.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
+	background-color: #F9FCFA;
+	border: none;
+	margin: 4px;
+	padding: 1px 0 0 8px;
+}
+
+.mdescLeft, .mdescRight {
+	padding: 0px 8px 4px 8px;
+	color: #555;
+}
+
+.memItemLeft, .memItemRight, .memTemplParams {
+	border-bottom: 1px solid #DEF0E7;
+}
+
+.memItemLeft, .memTemplItemLeft {
+        white-space: nowrap;
+}
+
+.memItemRight {
+	width: 100%;
+}
+
+.memTemplParams {
+	color: #46A274;
+        white-space: nowrap;
+}
+
+/* @end */
+
+/* @group Member Details */
+
+/* Styles for detailed member documentation */
+
+.memtemplate {
+	font-size: 80%;
+	color: #46A274;
+	font-weight: normal;
+	margin-left: 9px;
+}
+
+.memnav {
+	background-color: #EBF6F1;
+	border: 1px solid #A3D7BD;
+	text-align: center;
+	margin: 2px;
+	margin-right: 15px;
+	padding: 2px;
+}
+
+.mempage {
+	width: 100%;
+}
+
+.memitem {
+	padding: 0;
+	margin-bottom: 10px;
+	margin-right: 5px;
+        -webkit-transition: box-shadow 0.5s linear;
+        -moz-transition: box-shadow 0.5s linear;
+        -ms-transition: box-shadow 0.5s linear;
+        -o-transition: box-shadow 0.5s linear;
+        transition: box-shadow 0.5s linear;
+        display: table !important;
+        width: 100%;
+}
+
+.memitem.glow {
+         box-shadow: 0 0 15px cyan;
+}
+
+.memname {
+        font-weight: bold;
+        margin-left: 6px;
+}
+
+.memname td {
+	vertical-align: bottom;
+}
+
+.memproto, dl.reflist dt {
+        border-top: 1px solid #A8D9C0;
+        border-left: 1px solid #A8D9C0;
+        border-right: 1px solid #A8D9C0;
+        padding: 6px 0px 6px 0px;
+        color: #25553D;
+        font-weight: bold;
+        text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
+        background-image:url('nav_f.png');
+        background-repeat:repeat-x;
+        background-color: #E2F2EA;
+        /* opera specific markup */
+        box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+        border-top-right-radius: 4px;
+        border-top-left-radius: 4px;
+        /* firefox specific markup */
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+        -moz-border-radius-topright: 4px;
+        -moz-border-radius-topleft: 4px;
+        /* webkit specific markup */
+        -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+        -webkit-border-top-right-radius: 4px;
+        -webkit-border-top-left-radius: 4px;
+
+}
+
+.memdoc, dl.reflist dd {
+        border-bottom: 1px solid #A8D9C0;      
+        border-left: 1px solid #A8D9C0;      
+        border-right: 1px solid #A8D9C0; 
+        padding: 6px 10px 2px 10px;
+        background-color: #FBFDFC;
+        border-top-width: 0;
+        background-image:url('nav_g.png');
+        background-repeat:repeat-x;
+        background-color: #FFFFFF;
+        /* opera specific markup */
+        border-bottom-left-radius: 4px;
+        border-bottom-right-radius: 4px;
+        box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+        /* firefox specific markup */
+        -moz-border-radius-bottomleft: 4px;
+        -moz-border-radius-bottomright: 4px;
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+        /* webkit specific markup */
+        -webkit-border-bottom-left-radius: 4px;
+        -webkit-border-bottom-right-radius: 4px;
+        -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+}
+
+dl.reflist dt {
+        padding: 5px;
+}
+
+dl.reflist dd {
+        margin: 0px 0px 10px 0px;
+        padding: 5px;
+}
+
+.paramkey {
+	text-align: right;
+}
+
+.paramtype {
+	white-space: nowrap;
+}
+
+.paramname {
+	color: #602020;
+	white-space: nowrap;
+}
+.paramname em {
+	font-style: normal;
+}
+.paramname code {
+        line-height: 14px;
+}
+
+.params, .retval, .exception, .tparams {
+        margin-left: 0px;
+        padding-left: 0px;
+}       
+
+.params .paramname, .retval .paramname {
+        font-weight: bold;
+        vertical-align: top;
+}
+        
+.params .paramtype {
+        font-style: italic;
+        vertical-align: top;
+}       
+        
+.params .paramdir {
+        font-family: "courier new",courier,monospace;
+        vertical-align: top;
+}
+
+table.mlabels {
+	border-spacing: 0px;
+}
+
+td.mlabels-left {
+	width: 100%;
+	padding: 0px;
+}
+
+td.mlabels-right {
+	vertical-align: bottom;
+	padding: 0px;
+	white-space: nowrap;
+}
+
+span.mlabels {
+        margin-left: 8px;
+}
+
+span.mlabel {
+        background-color: #72C19A;
+        border-top:1px solid #53B484;
+        border-left:1px solid #53B484;
+        border-right:1px solid #C4E5D5;
+        border-bottom:1px solid #C4E5D5;
+	text-shadow: none;
+        color: white;
+        margin-right: 4px;
+        padding: 2px 3px;
+        border-radius: 3px;
+        font-size: 7pt;
+	white-space: nowrap;
+}
+
+
+
+/* @end */
+
+/* these are for tree view when not used as main index */
+
+div.directory {
+        margin: 10px 0px;
+        border-top: 1px solid #A8B8D9;
+        border-bottom: 1px solid #A8B8D9;
+        width: 100%;
+}
+
+.directory table {
+        border-collapse:collapse;
+}
+
+.directory td {
+        margin: 0px;
+        padding: 0px;
+	vertical-align: top;
+}
+
+.directory td.entry {
+        white-space: nowrap;
+        padding-right: 6px;
+}
+
+.directory td.entry a {
+        outline:none;
+}
+
+.directory td.entry a img {
+        border: none;
+}
+
+.directory td.desc {
+        width: 100%;
+        padding-left: 6px;
+	padding-right: 6px;
+	padding-top: 3px;
+	border-left: 1px solid rgba(0,0,0,0.05);
+}
+
+.directory tr.even {
+	padding-left: 6px;
+	background-color: #F7FBF9;
+}
+
+.directory img {
+	vertical-align: -30%;
+}
+
+.directory .levels {
+        white-space: nowrap;
+        width: 100%;
+        text-align: right;
+        font-size: 9pt;
+}
+
+.directory .levels span {
+        cursor: pointer;
+        padding-left: 2px;
+        padding-right: 2px;
+	color: #3D8C64;
+}
+
+div.dynheader {
+        margin-top: 8px;
+	-webkit-touch-callout: none;
+	-webkit-user-select: none;
+	-khtml-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+
+address {
+	font-style: normal;
+	color: #2A6146;
+}
+
+table.doxtable {
+	border-collapse:collapse;
+        margin-top: 4px;
+        margin-bottom: 4px;
+}
+
+table.doxtable td, table.doxtable th {
+	border: 1px solid #2D684A;
+	padding: 3px 7px 2px;
+}
+
+table.doxtable th {
+	background-color: #377F5B;
+	color: #FFFFFF;
+	font-size: 110%;
+	padding-bottom: 4px;
+	padding-top: 5px;
+}
+
+table.fieldtable {
+        width: 100%;
+        margin-bottom: 10px;
+        border: 1px solid #A8D9C0;
+        border-spacing: 0px;
+        -moz-border-radius: 4px;
+        -webkit-border-radius: 4px;
+        border-radius: 4px;
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+        -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+        box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+}
+
+.fieldtable td, .fieldtable th {
+        padding: 3px 7px 2px;
+}
+
+.fieldtable td.fieldtype, .fieldtable td.fieldname {
+        white-space: nowrap;
+        border-right: 1px solid #A8D9C0;
+        border-bottom: 1px solid #A8D9C0;
+        vertical-align: top;
+}
+
+.fieldtable td.fielddoc {
+        border-bottom: 1px solid #A8D9C0;
+        width: 100%;
+}
+
+.fieldtable tr:last-child td {
+        border-bottom: none;
+}
+
+.fieldtable th {
+        background-image:url('nav_f.png');
+        background-repeat:repeat-x;
+        background-color: #E2F2EA;
+        font-size: 90%;
+        color: #25553D;
+        padding-bottom: 4px;
+        padding-top: 5px;
+        text-align:left;
+        -moz-border-radius-topleft: 4px;
+        -moz-border-radius-topright: 4px;
+        -webkit-border-top-left-radius: 4px;
+        -webkit-border-top-right-radius: 4px;
+        border-top-left-radius: 4px;
+        border-top-right-radius: 4px;
+        border-bottom: 1px solid #A8D9C0;
+}
+
+
+.tabsearch {
+	top: 0px;
+	left: 10px;
+	height: 36px;
+	background-image: url('tab_b.png');
+	z-index: 101;
+	overflow: hidden;
+	font-size: 13px;
+}
+
+.navpath ul
+{
+	font-size: 11px;
+	background-image:url('tab_b.png');
+	background-repeat:repeat-x;
+	height:30px;
+	line-height:30px;
+	color:#8ACCAB;
+	border:solid 1px #C2E4D3;
+	overflow:hidden;
+	margin:0px;
+	padding:0px;
+}
+
+.navpath li
+{
+	list-style-type:none;
+	float:left;
+	padding-left:10px;
+	padding-right:15px;
+	background-image:url('bc_s.png');
+	background-repeat:no-repeat;
+	background-position:right;
+	color:#367C59;
+}
+
+.navpath li.navelem a
+{
+	height:32px;
+	display:block;
+	text-decoration: none;
+	outline: none;
+	font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif;
+}
+
+.navpath li.navelem a:hover
+{
+	color:#68BD92;
+}
+
+.navpath li.footer
+{
+        list-style-type:none;
+        float:right;
+        padding-left:10px;
+        padding-right:15px;
+        background-image:none;
+        background-repeat:no-repeat;
+        background-position:right;
+        color:#367C59;
+        font-size: 8pt;
+}
+
+
+div.summary
+{
+	float: right;
+	font-size: 8pt;
+	padding-right: 5px;
+	width: 50%;
+	text-align: right;
+}       
+
+div.summary a
+{
+	white-space: nowrap;
+}
+
+div.ingroups
+{
+	font-size: 8pt;
+	width: 50%;
+	text-align: left;
+}
+
+div.ingroups a
+{
+	white-space: nowrap;
+}
+
+div.header
+{
+        background-image:url('nav_h.png');
+        background-repeat:repeat-x;
+	background-color: #F9FCFA;
+	margin:  0px;
+	border-bottom: 1px solid #C4E5D5;
+}
+
+div.headertitle
+{
+	padding: 5px 5px 5px 10px;
+}
+
+dl
+{
+        padding: 0 0 0 10px;
+}
+
+/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */
+dl.section
+{
+	margin-left: 0px;
+	padding-left: 0px;
+}
+
+dl.note
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #D0C000;
+}
+
+dl.warning, dl.attention
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #FF0000;
+}
+
+dl.pre, dl.post, dl.invariant
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #00D000;
+}
+
+dl.deprecated
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #505050;
+}
+
+dl.todo
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #00C0E0;
+}
+
+dl.test
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #3030E0;
+}
+
+dl.bug
+{
+        margin-left:-7px;
+        padding-left: 3px;
+        border-left:4px solid;
+        border-color: #C08050;
+}
+
+dl.section dd {
+	margin-bottom: 6px;
+}
+
+
+#projectlogo
+{
+	text-align: center;
+	vertical-align: bottom;
+	border-collapse: separate;
+}
+ 
+#projectlogo img
+{ 
+	border: 0px none;
+}
+ 
+#projectname
+{
+	font: 300% Tahoma, Arial,sans-serif;
+	margin: 0px;
+	padding: 2px 0px;
+}
+    
+#projectbrief
+{
+	font: 120% Tahoma, Arial,sans-serif;
+	margin: 0px;
+	padding: 0px;
+}
+
+#projectnumber
+{
+	font: 50% Tahoma, Arial,sans-serif;
+	margin: 0px;
+	padding: 0px;
+}
+
+#titlearea
+{
+	padding: 0px;
+	margin: 0px;
+	width: 100%;
+	border-bottom: 1px solid #53B484;
+}
+
+.image
+{
+        text-align: center;
+}
+
+.dotgraph
+{
+        text-align: center;
+}
+
+.mscgraph
+{
+        text-align: center;
+}
+
+.caption
+{
+	font-weight: bold;
+}
+
+div.zoom
+{
+	border: 1px solid #90CEAF;
+}
+
+dl.citelist {
+        margin-bottom:50px;
+}
+
+dl.citelist dt {
+        color:#337554;
+        float:left;
+        font-weight:bold;
+        margin-right:10px;
+        padding:5px;
+}
+
+dl.citelist dd {
+        margin:2px 0;
+        padding:5px 0;
+}
+
+div.toc {
+        padding: 14px 25px;
+        background-color: #F4FAF7;
+        border: 1px solid #D8EEE3;
+        border-radius: 7px 7px 7px 7px;
+        float: right;
+        height: auto;
+        margin: 0 20px 10px 10px;
+        width: 200px;
+}
+
+div.toc li {
+        background: url("bdwn.png") no-repeat scroll 0 5px transparent;
+        font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif;
+        margin-top: 5px;
+        padding-left: 10px;
+        padding-top: 2px;
+}
+
+div.toc h3 {
+        font: bold 12px/1.2 Arial,FreeSans,sans-serif;
+	color: #46A274;
+        border-bottom: 0 none;
+        margin: 0;
+}
+
+div.toc ul {
+        list-style: none outside none;
+        border: medium none;
+        padding: 0px;
+}       
+
+div.toc li.level1 {
+        margin-left: 0px;
+}
+
+div.toc li.level2 {
+        margin-left: 15px;
+}
+
+div.toc li.level3 {
+        margin-left: 30px;
+}
+
+div.toc li.level4 {
+        margin-left: 45px;
+}
+
+.inherit_header {
+        font-weight: bold;
+        color: gray;
+        cursor: pointer;
+	-webkit-touch-callout: none;
+	-webkit-user-select: none;
+	-khtml-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+
+.inherit_header td {
+        padding: 6px 0px 2px 5px;
+}
+
+.inherit {
+        display: none;
+}
+
+tr.heading h2 {
+        margin-top: 12px;
+        margin-bottom: 4px;
+}
+
+ at media print
+{
+  #top { display: none; }
+  #side-nav { display: none; }
+  #nav-path { display: none; }
+  body { overflow:visible; }
+  h1, h2, h3, h4, h5, h6 { page-break-after: avoid; }
+  .summary { display: none; }
+  .memitem { page-break-inside: avoid; }
+  #doc-content
+  {
+    margin-left:0 !important;
+    height:auto !important;
+    width:auto !important;
+    overflow:inherit;
+    display:inline;
+  }
+}
+
diff --git a/sc/doc/error_policies.txt b/sc/doc/error_policies.txt
new file mode 100644
index 0000000..ae4d7ca
--- /dev/null
+++ b/sc/doc/error_policies.txt
@@ -0,0 +1,24 @@
+
+This document describes some policies on error handling.
+The SC library does not yet implement the SC_ASSERT distinctions.
+
+ * SC_ASSERT
+
+   Primarily used for internal development, failure on these asserts
+   indicates a likely internal bug that requires modification of the
+   library code by the developers.  These checks are only performed in
+   debug mode.
+
+ * SC_ASSERT_VERBOSE (does not yet exist...)
+
+   Used for checks that do not depend on third party software, but are
+   likely caused by improper use of the library by the user.  The verbose
+   error message and likely failure cause should be transparent to the
+   user, without knowledge of the library internals.  These checks are
+   only performed in debug mode.
+
+ * SC_CHECK_ABORT
+
+   Used when we are depending on third party software, such as MPI or
+   Trilinos, and updates to their code or different builds of the same
+   code could generate errors.  These should always be checked.
diff --git a/sc/doc/git/README b/sc/doc/git/README
new file mode 100644
index 0000000..730bd35
--- /dev/null
+++ b/sc/doc/git/README
@@ -0,0 +1,3 @@
+Here are some files related to using the libsc git repository.
+
+  hooks/       Directory containing useful hooks for the libsc project.
diff --git a/sc/doc/git/hooks/pre-commit b/sc/doc/git/hooks/pre-commit
new file mode 100755
index 0000000..778d905
--- /dev/null
+++ b/sc/doc/git/hooks/pre-commit
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# Called by git-commit with no arguments.  This checks to make
+# sure that all .c and .h files are indented correctly before
+# a commit is made.
+#
+# To enable this hook, make this file executable and place it
+# in $GIT_DIR/hooks.
+
+. git-sh-setup
+
+CHFILES=$(git-diff-index -M --name-only --cached HEAD | \
+          grep '.*\.[ch]$' | grep -v '^zlib/')
+
+for CHFILE in $CHFILES;
+do
+  MKTEMPLATE=`basename $CHFILE`.XXXXXXXX
+  TEMPFILE=`mktemp -t "$MKTEMPLATE"` || exit 1
+  $GIT_DIR/../scindent $GIT_DIR/../$CHFILE -o $TEMPFILE
+  if diff $GIT_DIR/../$CHFILE $TEMPFILE
+  then
+    rm -f $TEMPFILE
+  else
+    rm -f $TEMPFILE
+    NEEDS_FORMAT=1
+    echo >&2 "$CHFILE needs to be indented with:"
+    echo >&2 "   $GIT_DIR/../scindent \\"
+    echo >&2 "      $GIT_DIR/../$CHFILE"
+  fi
+done
+if [ -z "$NEEDS_FORMAT" ]
+then
+  exit 0
+else
+  exit 1
+fi
diff --git a/sc/doc/mainpage.dox b/sc/doc/mainpage.dox
new file mode 100644
index 0000000..4627bc8
--- /dev/null
+++ b/sc/doc/mainpage.dox
@@ -0,0 +1,67 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+/** \mainpage The sc auxiliary library
+ *
+ * \author Carsten Burstedde, Lucas C. Wilcox, Tobin Isaac et.al.
+ * \copyright GNU Lesser General Public License version 2.1
+ * (or, at your option, any later version)
+ *
+ * The sc library provides functions and data types that can be useful
+ * in scientific computing.  It is part of the p4est project, which uses
+ * it extensively.  Consequently, it is also a dependency of all projects
+ * that use p4est.  Some of the more important features are the following:
+ *
+ *  * The build system is based on the GNU autotools.  Several macro files
+ *    reside in the config/ subdirectory.  These are used in sc but also
+ *    available to other projects for convenience.  It is possible to nest
+ *    packages this way.
+ *  * The library provides MPI wrappers in case it is configured without
+ *    MPI.  They implement initialize/finalize and collective calls as
+ *    noops, which means that these do not have to be protected with the
+ *    `#ifdef SC_MPI` construct in the code.
+ *  * The library provides a logging framework that can be adapted by other
+ *    packages.  Multiple log levels are available, as well as options to
+ *    output on just one or all MPI processes.
+ *  * The library provides a set of data containers, such as dynamically
+ *    resizable arrays.
+ *
+ * To build the sc library from a tar distribution, use the standard
+ * procedure of the GNU autotools.  The configure script takes the following
+ * options:
+ *
+ * * `--enable-debug`   lowers the log level for increased verbosity and
+ *                    activates the `SC_ASSERT` macro for consistency checks.
+ * * `--enable-mpi`     pulls in the mpi.h include file and activates the MPI
+ *                    compiler wrappers.  If this option is not given, wrappers
+ *                    for MPI routines are used instead and the code is compiled
+ *                    in serial only.
+ * * `--disable-mpiio`  may be used to avoid using `MPI_File` based calls.
+ *
+ * A typical development configure line looks as follows:
+ * > `relative/path/to/configure CFLAGS="-Wall -O0 -g" --enable-mpi --enable-debug`
+ * A typical production configure line looks as follows:
+ * > `relative/path/to/configure CFLAGS="-Wall -O2" --enable-mpi`
+ *
+ * \see http://www.p4est.org/
+ * \see http://www.gnu.org/licenses/licenses.html
+ */
diff --git a/sc/doc/portability/README b/sc/doc/portability/README
new file mode 100644
index 0000000..03b75a2
--- /dev/null
+++ b/sc/doc/portability/README
@@ -0,0 +1,22 @@
+
+Portability of shell code (relevant especially for autotools scripting)
+-----------------------------------------------------------------------
+
+According to autoconf.html#Limitations-of-Builtins, this is worth noting:
+
+test may get confused by strings to compare that start with ! or - .
+This is the reason for using test "x$ABC" = xyes .
+
+test may not recognize -a or -o.  Use separate test calls instead,
+if test 1 || { test 2 && test 3 ; } ; then
+  :
+fi
+
+test may not recognize -z or -n.  Use a string check instead,
+test "x$ABC" = x
+test "x$ABC" != x
+
+! test -d ABC is not portable; test ! d ABC is.
+
+There is no need to use double quotes "" around backticks `` or the
+variable expansion case $ABC .
diff --git a/sc/doc/portability/isportm4.sh b/sc/doc/portability/isportm4.sh
new file mode 100755
index 0000000..650e59c
--- /dev/null
+++ b/sc/doc/portability/isportm4.sh
@@ -0,0 +1,11 @@
+#! /bin/sh
+
+FILE="$1"
+
+if test ! -f "$FILE" ; then
+	echo "Please specify file argument"
+	exit 1
+fi
+
+grep -P -v '^\s*dnl' "$FILE" | egrep \
+	'("x?(no|yes)|test\s+"?\$|test.+-(o|a)|!\s+test|test\s+x|"$x)'
diff --git a/sc/doc/portability/portitm4.sh b/sc/doc/portability/portitm4.sh
new file mode 100755
index 0000000..92cd9d9
--- /dev/null
+++ b/sc/doc/portability/portitm4.sh
@@ -0,0 +1,21 @@
+#! /bin/sh
+
+# Maybe it's best not to use this file, or to write it better.
+# It will certainly do bad things, such as changing the left hand side of an
+# equal sign and not the right side.
+
+echo 1>&2 "You should probably not use this script as is; see the source."
+
+FILE="$1"
+
+if test ! -f "$FILE" ; then
+	echo "Please specify file argument"
+	exit 1
+fi
+
+perl -p -i \
+	-e 'next if /^\s*dnl/;' \
+	-e 's,test\s+"\$,test "x\$,;' \
+	-e 's,=\s+"?x?(yes|no|unknown|disable)"?,= x\1,;' \
+	"$FILE"
+
diff --git a/sc/doc/portability/testfile.m4 b/sc/doc/portability/testfile.m4
new file mode 100644
index 0000000..b1f78ea
--- /dev/null
+++ b/sc/doc/portability/testfile.m4
@@ -0,0 +1,24 @@
+
+   dnl  euoahtns aoeu thnseuoa htnseoa  test "$ABC" = "no"
+dnl     euoahtns aoeu thnseuoa htnseoa  test "$ABC" = "no"
+
+dnl We don't need the quotes around literals
+test "$ABC" = "no"                                  # should hit 0
+test "$ABC" = "yes"                                 # should hit 1
+test "x$ABC" = "xno"                                # should hit 2
+test "x$ABC" = "xyes"                               # should hit 3
+
+dnl We need to prefix the $ABC with x
+test "$ABC" = no                                    # should hit 4
+test "$ABC" = yes                                   # should hit 5
+
+dnl These are ok
+test "x$ABC" = xno
+test "x$ABC" = xyes
+
+dnl It is not safe to use test with -o or -a; use test 1 && test 2 instead
+test 1 -a 2                                         # should hit 6
+test 1 -o 2                                         # should hit 7
+
+dnl It is not safe to use ! test; use test ! instead
+if ! test -d 3 ; then echo ABC ; fi                 # should hit 8
diff --git a/sc/example/bspline/Makefile.am b/sc/example/bspline/Makefile.am
new file mode 100644
index 0000000..53f251d
--- /dev/null
+++ b/sc/example/bspline/Makefile.am
@@ -0,0 +1,16 @@
+
+# This file is part of the SC Library
+# Makefile.am in example/bspline
+# included non-recursively from toplevel directory
+
+EXTRA_DIST += \
+        example/bspline/example1.txt \
+        example/bspline/example2.txt \
+        example/bspline/example3.txt
+
+bin_PROGRAMS += example/bspline/sc_bspline \
+	example/bspline/sc_bspline_per
+example_bspline_sc_bspline_SOURCES = example/bspline/bspline.c
+example_bspline_sc_bspline_per_SOURCES = example/bspline/bspline_per.c
+
+LINT_CSOURCES += $(example_bspline_sc_bspline_SOURCES)
diff --git a/sc/example/bspline/bspline.c b/sc/example/bspline/bspline.c
new file mode 100644
index 0000000..f035244
--- /dev/null
+++ b/sc/example/bspline/bspline.c
@@ -0,0 +1,208 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_bspline.h>
+
+static void
+create_plot (const char *name, sc_bspline_t * bs)
+{
+  int                 retval;
+  int                 i, nevals;
+  char                filename[BUFSIZ];
+  double             *result;
+  const double       *knotse;
+  FILE               *gf;
+
+  SC_INFOF ("Creating plot %s\n", name);
+
+  result = SC_ALLOC (double, SC_MAX (bs->d, 2));
+  knotse = bs->knots->e[0];
+
+  snprintf (filename, BUFSIZ, "%s.gnuplot", name);
+  gf = fopen (filename, "wb");
+  SC_CHECK_ABORT (gf != NULL, "Plot file open");
+
+  fprintf (gf, "set key left\n"
+           "set size ratio -1\n"
+           "set output \"%s.eps\"\n"
+           "set terminal postscript color solid\n", name);
+  fprintf (gf, "plot '-' title \"points\" with linespoints, "
+           "'-' title \"spline\" with lines, "
+           "'-' title \"knot values\", " "'-' title \"uniform values\"\n");
+
+  /* plot control points */
+  for (i = 0; i <= bs->p; ++i) {
+    fprintf (gf, "%g %g\n", bs->points->e[i][0], bs->points->e[i][1]);
+  }
+  fprintf (gf, "e\n");
+
+  /* plot spline curve */
+  nevals = 150;
+  for (i = 0; i < nevals; ++i) {
+    sc_bspline_evaluate (bs, i / (double) (nevals - 1), result);
+    fprintf (gf, "%g %g\n", result[0], result[1]);
+  }
+  fprintf (gf, "e\n");
+
+  /* plot spline points at knot values */
+  for (i = 0; i <= bs->l; ++i) {
+    sc_bspline_evaluate (bs, knotse[bs->n + i], result);
+    fprintf (gf, "%g %g\n", result[0], result[1]);
+  }
+  fprintf (gf, "e\n");
+
+  /* plot spline points at equidistant values */
+  for (i = 0; i <= bs->l; ++i) {
+    sc_bspline_evaluate (bs, i / (double) bs->l, result);
+    fprintf (gf, "%g %g\n", result[0], result[1]);
+  }
+  fprintf (gf, "e\n");
+
+  retval = fclose (gf);
+  SC_CHECK_ABORT (retval == 0, "Plot file close");
+
+  SC_FREE (result);
+}
+
+static void
+check_derivatives (sc_bspline_t * bs)
+{
+  int                 i, k;
+  int                 nevals;
+  double              t, h, diff, diff2;
+  double              result1[2], result2[2], result3[2], result4[2];
+
+  /* compare derivatives and finite difference approximation */
+  nevals = 150;
+  for (i = 0; i < nevals; ++i) {
+    t = i / (double) (nevals - 1);
+    sc_bspline_derivative (bs, t, result1);
+    sc_bspline_derivative2 (bs, t, result2);
+
+    diff = 0.;
+    for (k = 0; k < 2; ++k) {
+      diff += (result1[k] - result2[k]) * (result1[k] - result2[k]);
+    }
+    SC_CHECK_ABORT (diff < 1e-12, "Derivative mismatch");
+
+    if (i > 0 && i < nevals - 1) {
+      h = 1e-8;
+      sc_bspline_evaluate (bs, t - h, result2);
+      sc_bspline_evaluate (bs, t + h, result3);
+      sc_bspline_derivative_n (bs, 0, t + h, result4);
+
+      diff = diff2 = 0.;
+      for (k = 0; k < 2; ++k) {
+        result2[k] = (result3[k] - result2[k]) / (2. * h);
+        diff += (result1[k] - result2[k]) * (result1[k] - result2[k]);
+        diff2 += (result3[k] - result4[k]) * (result3[k] - result4[k]);
+      }
+      SC_CHECK_ABORT (diff < 1e-6, "Difference mismatch");
+      SC_CHECK_ABORT (diff2 < 1e-12, "Evaluation mismatch");
+    }
+
+#if 0
+    sc_bspline_derivative_n (bs, 2, t, result1);
+    SC_LDEBUGF ("Second derivative %d %g %g\n", i, result1[0], result1[1]);
+#endif
+  }
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret, mpisize;
+  int                 retval, nargs;
+  int                 minpoints;
+  int                 d, p, n;
+  double              x, y;
+  sc_dmatrix_t       *points, *knots, *works;
+  sc_bspline_t       *bs;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  mpicomm = sc_MPI_COMM_WORLD;
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  if (mpisize != 1)
+    sc_abort_collective ("This program runs in serial only");
+
+  nargs = 2;
+  if (argc != nargs) {
+    SC_LERRORF ("Usage: %s <degree>\n", argv[0]);
+    SC_ABORT ("Usage error");
+  }
+  n = atoi (argv[1]);
+  SC_CHECK_ABORT (n >= 0, "Degree must be non-negative");
+
+  minpoints = sc_bspline_min_number_points (n);
+  SC_INFOF ("Degree %d will require at least %d points\n", n, minpoints);
+
+  d = 2;
+  points = sc_dmatrix_new (0, d);
+
+  p = -1;
+  for (;;) {
+    retval = scanf ("%lg %lg", &x, &y);
+    if (retval == d) {
+      ++p;
+      sc_dmatrix_resize (points, p + 1, d);
+      points->e[p][0] = x;
+      points->e[p][1] = y;
+    }
+    else
+      break;
+  }
+  SC_CHECK_ABORT (p + 1 >= minpoints, "Not enough points");
+  SC_INFOF ("Points read %d\n", p + 1);
+
+  works = sc_bspline_workspace_new (n, d);
+
+  knots = sc_bspline_knots_new (n, points);
+  bs = sc_bspline_new (n, points, knots, works);
+  create_plot ("uniform", bs);
+  sc_bspline_destroy (bs);
+  sc_dmatrix_destroy (knots);
+
+  if (n > 0) {
+    knots = sc_bspline_knots_new_length (n, points);
+    bs = sc_bspline_new (n, points, knots, works);
+    create_plot ("length", bs);
+    check_derivatives (bs);
+    sc_bspline_destroy (bs);
+    sc_dmatrix_destroy (knots);
+  }
+
+  sc_dmatrix_destroy (works);
+  sc_dmatrix_destroy (points);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/example/bspline/bspline_per.c b/sc/example/bspline/bspline_per.c
new file mode 100644
index 0000000..aa966bd
--- /dev/null
+++ b/sc/example/bspline/bspline_per.c
@@ -0,0 +1,209 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_bspline.h>
+
+static void
+create_plot (const char *name, sc_bspline_t * bs)
+{
+  int                 retval;
+  int                 i, nevals;
+  char                filename[BUFSIZ];
+  double             *result;
+  const double       *knotse;
+  FILE               *gf;
+
+  SC_INFOF ("Creating plot %s\n", name);
+
+  result = SC_ALLOC (double, SC_MAX (bs->d, 2));
+  knotse = bs->knots->e[0];
+
+  snprintf (filename, BUFSIZ, "%s.gnuplot", name);
+  gf = fopen (filename, "wb");
+  SC_CHECK_ABORT (gf != NULL, "Plot file open");
+
+  fprintf (gf, "set key left\n"
+           "set size ratio -1\n"
+           "set output \"%s.eps\"\n"
+           "set terminal postscript color solid\n", name);
+  fprintf (gf, "plot '-' title \"points\" with linespoints, "
+           "'-' title \"spline\" with lines, "
+           "'-' title \"knot values\", " "'-' title \"uniform values\"\n");
+
+  /* plot control points */
+  for (i = 0; i <= bs->p; ++i) {
+    fprintf (gf, "%g %g\n", bs->points->e[i][0], bs->points->e[i][1]);
+  }
+  fprintf (gf, "e\n");
+
+  /* plot spline curve */
+  nevals = 150;
+  for (i = 0; i < nevals; ++i) {
+    sc_bspline_evaluate (bs, i / (double) (nevals - 1), result);
+    fprintf (gf, "%g %g\n", result[0], result[1]);
+  }
+  fprintf (gf, "e\n");
+
+  /* plot spline points at knot values */
+  for (i = 0; i <= bs->l; ++i) {
+    sc_bspline_evaluate (bs, knotse[bs->n + i], result);
+    fprintf (gf, "%g %g\n", result[0], result[1]);
+  }
+  fprintf (gf, "e\n");
+
+  /* plot spline points at equidistant values */
+  for (i = 0; i <= bs->l; ++i) {
+    sc_bspline_evaluate (bs, i / (double) bs->l, result);
+    fprintf (gf, "%g %g\n", result[0], result[1]);
+  }
+  fprintf (gf, "e\n");
+
+  retval = fclose (gf);
+  SC_CHECK_ABORT (retval == 0, "Plot file close");
+
+  SC_FREE (result);
+}
+
+static void
+check_derivatives (sc_bspline_t * bs)
+{
+  int                 i, k;
+  int                 nevals;
+  double              t, h, diff, diff2;
+  double              result1[2], result2[2], result3[2], result4[2];
+
+  /* compare derivatives and finite difference approximation */
+  nevals = 150;
+  for (i = 0; i < nevals; ++i) {
+    t = i / (double) (nevals - 1);
+    sc_bspline_derivative (bs, t, result1);
+    sc_bspline_derivative2 (bs, t, result2);
+
+    diff = 0.;
+    for (k = 0; k < 2; ++k) {
+      diff += (result1[k] - result2[k]) * (result1[k] - result2[k]);
+    }
+    SC_CHECK_ABORT (diff < 1e-12, "Derivative mismatch");
+
+    if (i > 0 && i < nevals - 1) {
+      h = 1e-8;
+      sc_bspline_evaluate (bs, t - h, result2);
+      sc_bspline_evaluate (bs, t + h, result3);
+      sc_bspline_derivative_n (bs, 0, t + h, result4);
+
+      diff = diff2 = 0.;
+      for (k = 0; k < 2; ++k) {
+        result2[k] = (result3[k] - result2[k]) / (2. * h);
+        diff += (result1[k] - result2[k]) * (result1[k] - result2[k]);
+        diff2 += (result3[k] - result4[k]) * (result3[k] - result4[k]);
+      }
+      SC_CHECK_ABORT (diff < 1e-6, "Difference mismatch");
+      SC_CHECK_ABORT (diff2 < 1e-12, "Evaluation mismatch");
+    }
+
+#if 0
+    sc_bspline_derivative_n (bs, 2, t, result1);
+    SC_LDEBUGF ("Second derivative %d %g %g\n", i, result1[0], result1[1]);
+#endif
+  }
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret, mpisize;
+  int                 retval, nargs;
+  int                 minpoints;
+  int                 d, p, n;
+  double              x, y;
+  sc_dmatrix_t       *points, *knots, *works;
+  sc_bspline_t       *bs;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  mpicomm = sc_MPI_COMM_WORLD;
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  if (mpisize != 1)
+    sc_abort_collective ("This program runs in serial only");
+
+  nargs = 2;
+  if (argc != nargs) {
+    SC_LERRORF ("Usage: %s <degree>\n", argv[0]);
+    SC_ABORT ("Usage error");
+  }
+  n = atoi (argv[1]);
+  SC_CHECK_ABORT (n > 0, "Degree must be positive");
+
+  minpoints = SC_MAX (3, n);
+  SC_INFOF ("Degree %d will require at least %d points\n", n, minpoints);
+
+  d = 2;
+  points = sc_dmatrix_new (0, d);
+
+  p = -1;
+  for (;;) {
+    retval = scanf ("%lg %lg", &x, &y);
+    if (retval == d) {
+      ++p;
+      sc_dmatrix_resize (points, p + 1, d);
+      points->e[p][0] = x;
+      points->e[p][1] = y;
+    }
+    else
+      break;
+  }
+  SC_CHECK_ABORT (p + 1 >= minpoints, "Not enough points");
+  SC_INFOF ("Points read %d\n", p + 1);
+
+  works = sc_bspline_workspace_new (n, d);
+  sc_bspline_make_points_periodic (n, points);
+
+  knots = sc_bspline_knots_new_periodic (n, points);
+  bs = sc_bspline_new (n, points, knots, works);
+  create_plot ("uniform", bs);
+  sc_bspline_destroy (bs);
+  sc_dmatrix_destroy (knots);
+
+  if (n > 0) {
+    knots = sc_bspline_knots_new_length_periodic (n, points);
+    bs = sc_bspline_new (n, points, knots, works);
+    create_plot ("length", bs);
+    check_derivatives (bs);
+    sc_bspline_destroy (bs);
+    sc_dmatrix_destroy (knots);
+  }
+
+  sc_dmatrix_destroy (works);
+  sc_dmatrix_destroy (points);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/example/bspline/example1.txt b/sc/example/bspline/example1.txt
new file mode 100644
index 0000000..eaaf8e2
--- /dev/null
+++ b/sc/example/bspline/example1.txt
@@ -0,0 +1,15 @@
+	-.9	1.4
+        -.95    1.3
+        -.9     1.2
+        -.5     1.5
+        1       1
+        1.5     2
+        1.5     2.3
+        1.3     4
+        -.9     3
+        .5      2.5
+        .8      2.5
+        1.1     2.3
+        1.0     2
+        0       2.1
+        -0.1    2.2
diff --git a/sc/example/bspline/example2.txt b/sc/example/bspline/example2.txt
new file mode 100644
index 0000000..b84163b
--- /dev/null
+++ b/sc/example/bspline/example2.txt
@@ -0,0 +1,15 @@
+2	0
+3	.1
+4	.6
+5	.9
+6	1
+7	1.5
+8	2
+9	2.1
+10	2
+11	1.5
+12	1
+13	.9
+14	.6
+15	.1
+16	0
diff --git a/sc/example/bspline/example3.txt b/sc/example/bspline/example3.txt
new file mode 100644
index 0000000..7ae8308
--- /dev/null
+++ b/sc/example/bspline/example3.txt
@@ -0,0 +1,14 @@
+1	4
+2	3
+8	1
+6	2
+10	4
+7	12
+18	3
+7	6
+6	12
+14	1
+15	3
+3	8
+5	11
+16	10
diff --git a/sc/example/cuda/Makefile.am b/sc/example/cuda/Makefile.am
new file mode 100644
index 0000000..9f4f279
--- /dev/null
+++ b/sc/example/cuda/Makefile.am
@@ -0,0 +1,14 @@
+
+# This file is part of the SC Library
+# Makefile.am in cuda
+# included non-recursively from toplevel directory
+
+# if SC_WITH_CUDA
+#
+# bin_PROGRAMS += example/cuda/sc_aprog
+#
+# example_cuda_sc_aprog_SOURCES = example/cuda/aprog.c
+#
+# example_cuda_sc_aprog_LDADD = @NVCCLIBS@
+#
+# endif
diff --git a/sc/example/dmatrix/Makefile.am b/sc/example/dmatrix/Makefile.am
new file mode 100644
index 0000000..48efc54
--- /dev/null
+++ b/sc/example/dmatrix/Makefile.am
@@ -0,0 +1,9 @@
+
+# This file is part of the SC Library
+# Makefile.am in example/dmatrix
+# included non-recursively from toplevel directory
+
+bin_PROGRAMS += example/dmatrix/sc_dmatrix
+example_dmatrix_sc_dmatrix_SOURCES = example/dmatrix/dmatrix.c
+
+LINT_CSOURCES += $(example_dmatrix_sc_dmatrix_SOURCES)
diff --git a/sc/example/dmatrix/dmatrix.c b/sc/example/dmatrix/dmatrix.c
new file mode 100644
index 0000000..784f93d
--- /dev/null
+++ b/sc/example/dmatrix/dmatrix.c
@@ -0,0 +1,168 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_dmatrix.h>
+
+static void
+check_matrix_vector (void)
+{
+  int                 i, j, k;
+  double              d[23];
+  sc_dmatrix_t       *A, *X3, *X4, *Y3a, *Y3b, *Y4a, *Y4b;
+
+  for (k = 0; k < 23; ++k) {
+    d[k] = (k + 3) * (k - 3 * k) + sqrt (2. + sin (k * 1.22345));
+  }
+
+  A = sc_dmatrix_new (3, 4);
+  X3 = sc_dmatrix_new (3, 1);
+  X4 = sc_dmatrix_new (1, 4);
+  Y3a = sc_dmatrix_new (3, 1);
+  Y3b = sc_dmatrix_new (1, 3);
+  Y4a = sc_dmatrix_new (4, 1);
+  Y4b = sc_dmatrix_new (1, 4);
+
+  for (i = 0; i < 3; ++i) {
+    X3->e[i][0] = i + 1.;
+  }
+  for (j = 0; j < 4; ++j) {
+    X4->e[0][j] = j + 1.;
+  }
+
+  k = 0;
+  for (i = 0; i < 3; ++i) {
+    for (j = 0; j < 4; ++j) {
+      A->e[i][j] = d[k];
+      k = (k + 1) % 23;
+    }
+  }
+
+  sc_dmatrix_vector (SC_NO_TRANS, SC_TRANS, SC_NO_TRANS, 1., A, X4, 0., Y3a);
+  sc_dmatrix_vector (SC_NO_TRANS, SC_TRANS, SC_NO_TRANS, 2., A, X4, 2., Y3a);
+  sc_dmatrix_vector (SC_NO_TRANS, SC_TRANS, SC_TRANS, 4., A, X4, 0., Y3b);
+  for (i = 0; i < 3; ++i) {
+    Y3b->e[0][i] -= Y3a->e[i][0];
+  }
+  sc_dmatrix_multiply (SC_NO_TRANS, SC_TRANS, 8., A, X4, -2., Y3a);
+  printf ("0 =\n");
+  sc_dmatrix_write (Y3a, stdout);
+  printf ("0 =\n");
+  sc_dmatrix_write (Y3b, stdout);
+
+  sc_dmatrix_vector (SC_TRANS, SC_NO_TRANS, SC_NO_TRANS, 1., A, X3, 0., Y4a);
+  sc_dmatrix_vector (SC_TRANS, SC_NO_TRANS, SC_NO_TRANS, 1., A, X3, 2., Y4a);
+  sc_dmatrix_vector (SC_TRANS, SC_NO_TRANS, SC_TRANS, 3., A, X3, 0., Y4b);
+  for (j = 0; j < 4; ++j) {
+    Y4b->e[0][j] -= Y4a->e[j][0];
+  }
+  sc_dmatrix_multiply (SC_TRANS, SC_NO_TRANS, 3., A, X3, -1., Y4a);
+  printf ("0 =\n");
+  sc_dmatrix_write (Y4a, stdout);
+  printf ("0 =\n");
+  sc_dmatrix_write (Y4b, stdout);
+
+  sc_dmatrix_destroy (A);
+  sc_dmatrix_destroy (X3);
+  sc_dmatrix_destroy (X4);
+  sc_dmatrix_destroy (Y3a);
+  sc_dmatrix_destroy (Y3b);
+  sc_dmatrix_destroy (Y4a);
+  sc_dmatrix_destroy (Y4b);
+}
+
+static void
+check_matrix_multiply ()
+{
+  sc_dmatrix_t       *A, *B, *C, *D, *E, *vA;
+  double              alpha, beta;
+
+  A = sc_dmatrix_new (3, 2);
+  B = sc_dmatrix_new (2, 3);
+  C = sc_dmatrix_new (3, 3);
+
+  A->e[0][0] = 1;
+  A->e[0][1] = 2;
+  A->e[1][0] = 3;
+  A->e[1][1] = 4;
+  A->e[2][0] = 5;
+  A->e[2][1] = 6;
+  printf ("A =\n");
+  sc_dmatrix_write (A, stdout);
+
+  B->e[0][0] = 1;
+  B->e[0][1] = 7;
+  B->e[0][2] = 2;
+  B->e[1][0] = 3;
+  B->e[1][1] = 5;
+  B->e[1][2] = 4;
+  printf ("B =\n");
+  sc_dmatrix_write (B, stdout);
+
+  alpha = 1.0;
+  beta = 0.0;
+
+  sc_dmatrix_multiply (SC_NO_TRANS, SC_NO_TRANS, alpha, A, B, beta, C);
+  printf ("C =\n");
+  sc_dmatrix_write (C, stdout);
+
+  D = sc_dmatrix_new (2, 3);
+
+  sc_dmatrix_multiply (SC_TRANS, SC_NO_TRANS, alpha, A, C, beta, D);
+  printf ("D =\n");
+  sc_dmatrix_write (D, stdout);
+
+  E = sc_dmatrix_new (2, 2);
+
+  sc_dmatrix_multiply (SC_NO_TRANS, SC_TRANS, alpha, D, B, beta, E);
+  printf ("E =\n");
+  sc_dmatrix_write (E, stdout);
+
+  vA = sc_dmatrix_new_view (3, 2, A);
+
+  sc_dmatrix_multiply (SC_NO_TRANS, SC_NO_TRANS, alpha, vA, B, beta, C);
+  printf ("C =\n");
+  sc_dmatrix_write (C, stdout);
+
+  sc_dmatrix_reshape (vA, 2, 3);
+  printf ("reshape(2, 3, vA) =\n");
+  sc_dmatrix_write (vA, stdout);
+
+  sc_dmatrix_destroy (vA);
+  sc_dmatrix_destroy (A);
+  sc_dmatrix_destroy (B);
+  sc_dmatrix_destroy (C);
+  sc_dmatrix_destroy (D);
+  sc_dmatrix_destroy (E);
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_init (sc_MPI_COMM_NULL, 1, 1, NULL, SC_LP_DEFAULT);
+
+  check_matrix_vector ();
+  check_matrix_multiply ();
+
+  sc_finalize ();
+
+  return 0;
+}
diff --git a/sc/example/function/Makefile.am b/sc/example/function/Makefile.am
new file mode 100644
index 0000000..7b48c6c
--- /dev/null
+++ b/sc/example/function/Makefile.am
@@ -0,0 +1,9 @@
+
+# This file is part of the SC Library
+# Makefile.am in example/function
+# included non-recursively from toplevel directory
+
+bin_PROGRAMS += example/function/sc_function
+example_function_sc_function_SOURCES = example/function/function.c
+
+LINT_CSOURCES += $(example_function_sc_function_SOURCES)
diff --git a/sc/example/function/function.c b/sc/example/function/function.c
new file mode 100644
index 0000000..4b68fde
--- /dev/null
+++ b/sc/example/function/function.c
@@ -0,0 +1,62 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_functions.h>
+
+static double
+func_sqr (double x, void *data)
+{
+  return x * x;
+}
+
+static double
+func_sqrt (double x, void *data)
+{
+  return sqrt (x);
+}
+
+static double
+func_sin (double x, void *data)
+{
+  return sin (2. * M_PI * x);
+}
+
+int
+main (int argc, char **argv)
+{
+  double              x1, x2, x3;
+  double              y1, y2, y3;
+
+  y1 = .56;
+  x1 = sc_function1_invert (func_sqr, NULL, 0., 1., y1, 1e-3);
+  SC_STATISTICSF ("Inverted sqr (%g) = %g\n", x1, y1);
+
+  y2 = .56;
+  x2 = sc_function1_invert (func_sqrt, NULL, 0., 1., y2, 1e-3);
+  SC_STATISTICSF ("Inverted sqrt (%g) = %g\n", x2, y2);
+
+  y3 = .56;
+  x3 = sc_function1_invert (func_sin, NULL, 0.25, 0.75, y3, 1e-3);
+  SC_STATISTICSF ("Inverted sin (2 pi %g) = %g\n", x3, y3);
+
+  return 0;
+}
diff --git a/sc/example/logging/Makefile.am b/sc/example/logging/Makefile.am
new file mode 100644
index 0000000..49b9c23
--- /dev/null
+++ b/sc/example/logging/Makefile.am
@@ -0,0 +1,9 @@
+
+# This file is part of the SC Library
+# Makefile.am in example/logging
+# included non-recursively from toplevel directory
+
+bin_PROGRAMS += example/logging/sc_logging
+example_logging_sc_logging_SOURCES = example/logging/logging.c
+
+LINT_CSOURCES += $(example_logging_sc_logging_SOURCES)
diff --git a/sc/example/logging/logging.c b/sc/example/logging/logging.c
new file mode 100644
index 0000000..de14e10
--- /dev/null
+++ b/sc/example/logging/logging.c
@@ -0,0 +1,88 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc.h>
+
+static void
+log_normal (void)
+{
+  SC_TRACEF ("Trace normal %d\n", SC_LP_TRACE);
+  SC_LDEBUG ("Debug normal\n");
+  SC_VERBOSEF ("Verbose normal %d\n", SC_LP_VERBOSE);
+  SC_INFO ("Info normal\n");
+  SC_STATISTICSF ("Statistics normal %d\n", SC_LP_STATISTICS);
+  SC_PRODUCTION ("Production normal\n");
+  SC_ESSENTIAL ("Essential normal\n");
+  SC_LERRORF ("Error normal %d\n", SC_LP_ERROR);
+}
+
+static void
+log_global (void)
+{
+  SC_GLOBAL_TRACE ("Trace global\n");
+  SC_GLOBAL_LDEBUGF ("Debug global %d\n", SC_LP_DEBUG);
+  SC_GLOBAL_VERBOSE ("Verbose global\n");
+  SC_GLOBAL_INFOF ("Info global %d\n", SC_LP_INFO);
+  SC_GLOBAL_STATISTICS ("Statistics global\n");
+  SC_GLOBAL_PRODUCTIONF ("Production global %d\n", SC_LP_PRODUCTION);
+  SC_GLOBAL_ESSENTIALF ("Essential global %d\n", SC_LP_ESSENTIAL);
+  SC_GLOBAL_LERROR ("Error global\n");
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  log_normal ();
+  log_global ();
+
+  sc_set_log_defaults (stdout, NULL, SC_LP_VERBOSE);
+  log_normal ();
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+
+  sc_package_print_summary (SC_LP_PRODUCTION);
+
+  sc_set_log_defaults (stderr, NULL, SC_LP_STATISTICS);
+  log_normal ();
+  log_global ();
+
+  sc_set_log_defaults (stdout, NULL, SC_LP_TRACE);
+  log_normal ();
+
+  sc_set_log_defaults (NULL, NULL, SC_LP_TRACE);
+  log_global ();
+
+  sc_set_log_defaults (stderr, NULL, SC_LP_SILENT);
+  log_normal ();
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/example/options/Makefile.am b/sc/example/options/Makefile.am
new file mode 100644
index 0000000..b2aeefd
--- /dev/null
+++ b/sc/example/options/Makefile.am
@@ -0,0 +1,11 @@
+
+# This file is part of the SC Library
+# Makefile.am in example/options
+# included non-recursively from toplevel directory
+
+bin_PROGRAMS += example/options/sc_options
+example_options_sc_options_SOURCES = example/options/options.c
+
+dist_scini_DATA += example/options/options.ini example/options/preload.ini
+
+LINT_CSOURCES += $(example_options_sc_options_SOURCES)
diff --git a/sc/example/options/options.c b/sc/example/options/options.c
new file mode 100644
index 0000000..fde4d7c
--- /dev/null
+++ b/sc/example/options/options.c
@@ -0,0 +1,129 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_options.h>
+
+static int
+callback (sc_options_t * opt, const char *theoptarg, void *data)
+{
+  if (theoptarg == NULL) {
+    SC_GLOBAL_INFOF ("%s without argument\n", (const char *) data);
+  }
+  else {
+    SC_GLOBAL_INFOF ("%s with %s\n", (const char *) data, theoptarg);
+  }
+
+  return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret, retval;
+  int                 rank;
+  int                 first_arg;
+  int                 w;
+  int                 i1, i2, si1;
+  size_t              z;
+  double              d, sd;
+  const char         *s1, *s2, *ss1, *ss2;
+  const char         *cd = "Callback example";
+  sc_options_t       *opt, *subopt;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  mpiret = sc_MPI_Comm_rank (sc_MPI_COMM_WORLD, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+
+  opt = sc_options_new (argv[0]);
+  sc_options_add_switch (opt, 'w', "switch", &w, "Switch");
+  sc_options_add_int (opt, 'i', "integer1", &i1, 0, "Integer 1");
+  sc_options_add_double (opt, 'd', "double", &d, 0., "Double");
+  sc_options_add_string (opt, 's', "string", &s1, NULL, "String 1");
+  sc_options_add_callback (opt, 'c', "call1", 1, callback, (void *) cd,
+                           "Callback 1");
+  sc_options_add_callback (opt, 'C', "call2", 0, callback, (void *) cd,
+                           "Callback 2");
+  sc_options_add_string (opt, 't', NULL, &s2, NULL, "String 2");
+  sc_options_add_inifile (opt, 'f', "inifile", ".ini file");
+  sc_options_add_int (opt, '\0', "integer2", &i2, 7, "Integer 2");
+  sc_options_add_size_t (opt, 'z', "sizet", &z, (size_t) 7000000000ULL, "Size_t");
+
+  subopt = sc_options_new (argv[0]);
+  sc_options_add_int (subopt, 'i', "integer", &si1, 0, "Subset integer");
+  sc_options_add_double (subopt, 'd', "double", &sd, 0., "Subset double");
+  sc_options_add_string (subopt, 's', NULL, &ss1, NULL, "Subset string 1");
+  sc_options_add_string (subopt, '\0', "string2", &ss2, NULL,
+                         "Subset string 1");
+
+  sc_options_add_suboptions (opt, subopt, "Subset");
+
+  /* this is just to show off the load function */
+  if (!sc_options_load (sc_package_id, SC_LP_INFO, opt, "preload.ini")) {
+    SC_GLOBAL_INFO ("Preload successful\n");
+  }
+  else {
+    SC_GLOBAL_INFO ("Preload not found or failed\n");
+  }
+
+  first_arg = sc_options_parse (sc_package_id, SC_LP_INFO, opt, argc, argv);
+  if (first_arg < 0) {
+    sc_options_print_usage (sc_package_id, SC_LP_INFO, opt,
+                            "This is arg 1\nand this is arg 2");
+    SC_GLOBAL_INFO ("Option parsing failed\n");
+  }
+  else {
+    SC_GLOBAL_INFO ("Option parsing successful\n");
+    sc_options_print_summary (sc_package_id, SC_LP_INFO, opt);
+
+    if (rank == 0) {
+      retval = sc_options_save (sc_package_id, SC_LP_INFO, opt, "output.ini");
+      if (retval) {
+        SC_GLOBAL_INFO ("Option file output failed\n");
+      }
+      else {
+        retval = sc_options_load_args (sc_package_id, SC_LP_INFO, opt,
+                                       "output.ini");
+        if (retval) {
+          SC_GLOBAL_INFO ("Argument file input failed\n");
+        }
+        else {
+          sc_options_print_summary (sc_package_id, SC_LP_INFO, opt);
+          SC_GLOBAL_INFO ("Argument save load successful\n");
+        }
+      }
+    }
+  }
+
+  sc_options_destroy (opt);
+  sc_options_destroy (subopt);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/example/options/options.ini b/sc/example/options/options.ini
new file mode 100644
index 0000000..6ba89b9
--- /dev/null
+++ b/sc/example/options/options.ini
@@ -0,0 +1,11 @@
+# This file is for playing around with the -f option.
+
+[Options]
+	-d = 7.55
+	switch = true
+	string = This string is read from the options.ini file!
+[Subset]
+  integer = 23
+  double = 3.62
+  -s = Subset string 1
+  string2 = subset string 2
diff --git a/sc/example/options/preload.ini b/sc/example/options/preload.ini
new file mode 100644
index 0000000..b5a6f72
--- /dev/null
+++ b/sc/example/options/preload.ini
@@ -0,0 +1,4 @@
+# This file is for playing around with the load function.
+
+[Options]
+	-d = 3.52
diff --git a/sc/example/pthread/Makefile.am b/sc/example/pthread/Makefile.am
new file mode 100644
index 0000000..4626f4b
--- /dev/null
+++ b/sc/example/pthread/Makefile.am
@@ -0,0 +1,15 @@
+
+# This file is part of the SC Library
+# Makefile.am in example/pthread
+# included non-recursively from toplevel directory
+
+if SC_ENABLE_PTHREAD
+
+bin_PROGRAMS += example/pthread/sc_pthread example/pthread/sc_condvar
+example_pthread_sc_pthread_SOURCES = example/pthread/pthread.c
+example_pthread_sc_condvar_SOURCES = example/pthread/condvar.c
+
+LINT_CSOURCES += $(example_pthread_sc_pthread_SOURCES) \
+                 $(example_pthread_sc_condvar_SOURCES)
+
+endif
diff --git a/sc/example/pthread/condvar.c b/sc/example/pthread/condvar.c
new file mode 100644
index 0000000..29a84d8
--- /dev/null
+++ b/sc/example/pthread/condvar.c
@@ -0,0 +1,309 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <pthread.h>
+#include <sc_options.h>
+
+typedef struct global_data global_data_t;
+
+typedef struct thread_data
+{
+  int                 id;
+  int                 working, done;
+  pthread_t           thread;
+  global_data_t      *gd;
+}
+thread_data_t;
+
+struct global_data
+{
+  int                 N, T;
+  int                 setup;
+  int                 scheduled, started;
+  pthread_mutex_t     mutex;
+  pthread_cond_t      cond_setup;
+  pthread_cond_t      cond_start;
+  pthread_cond_t      cond_stop;
+  pthread_attr_t      attr;
+  thread_data_t      *td;
+};
+
+static void        *
+start_thread (void *v)
+{
+  thread_data_t      *td = (thread_data_t *) v;
+  global_data_t      *g = td->gd;
+  int                 j;
+
+  /* setup phase do work here depending on td->i */
+  SC_INFOF ("T%02d setup working\n", td->id);
+
+  /* setup phase end: increment global state */
+  pthread_mutex_lock (&g->mutex);
+  ++g->setup;
+  pthread_mutex_unlock (&g->mutex);
+  pthread_cond_signal (&g->cond_setup);
+
+  SC_INFOF ("T%02d setup done\n", td->id);
+
+  for (j = 0;; ++j) {
+
+    SC_INFOF ("T%02d task waiting\n", td->id);
+
+    /* task phase begin: wait for start or exit signal */
+    pthread_mutex_lock (&g->mutex);
+    while (g->scheduled == g->started) {
+      SC_INFOF ("T%02d task into cond_wait with %d\n", td->id, g->scheduled);
+      pthread_cond_wait (&g->cond_start, &g->mutex);
+    }
+    SC_INFOF ("T%02d task skip cond_wait\n", td->id);
+    if (g->scheduled == -1) {
+      SC_INFOF ("T%02d task to exit\n", td->id);
+      pthread_mutex_unlock (&g->mutex);
+      pthread_exit (NULL);
+    }
+
+    /* set this task to work */
+    SC_ASSERT (0 <= g->started);
+    SC_ASSERT (g->started < g->scheduled);
+    SC_ASSERT (g->scheduled <= g->N);
+    SC_INFOF ("T%02d task now is id %d\n", td->id, g->started);
+
+    /* this thread picks the next scheduled id */
+    td = &g->td[g->started++];
+    SC_ASSERT (td->working == 0);
+    SC_ASSERT (td->done == 0);
+    td->working = 1;
+    pthread_mutex_unlock (&g->mutex);
+
+    /* task phase do work here depending on td->i */
+    SC_INFOF ("T%02d task working\n", td->id);
+
+    /* signal that the work is done */
+    pthread_mutex_lock (&g->mutex);
+    SC_ASSERT (td->done == 0);
+    SC_ASSERT (td->working == 1);
+    td->done = 1;
+    td->working = 0;
+    pthread_mutex_unlock (&g->mutex);
+    pthread_cond_signal (&g->cond_stop);
+
+    SC_INFOF ("T%02d task done\n", td->id);
+  }
+
+  pthread_exit (v);
+}
+
+static void
+condvar_setup (global_data_t * g)
+{
+  int                 i;
+  int                 pth;
+  thread_data_t      *td;
+
+  /*
+   * The main thread starts worker threads.
+   * The worker threads do their setup work in undefined order.
+   * The main thread waits until all of them are are done with their setup.
+   * The threads go to sleep and the main thread does stuff for a while.
+   * The main thread then gives each worker something to do in order.
+   * The main thread waits for the workers to finish in reverse order.
+   */
+
+  SC_INFO ("Main setup begin\n");
+
+  /* start threads */
+  g->setup = 0;
+  g->scheduled = g->started = 0;
+  pthread_mutex_init (&g->mutex, NULL);
+  pthread_cond_init (&g->cond_setup, NULL);
+  pthread_cond_init (&g->cond_start, NULL);
+  pthread_cond_init (&g->cond_stop, NULL);
+  pthread_attr_init (&g->attr);
+  pthread_attr_setdetachstate (&g->attr, PTHREAD_CREATE_JOINABLE);
+  g->td = SC_ALLOC (thread_data_t, g->N);
+  for (i = 0; i < g->N; ++i) {
+    td = &g->td[i];
+    td->id = i;
+    td->gd = g;
+    td->working = td->done = 0;
+    pth = pthread_create (&td->thread, &g->attr, &start_thread, td);
+    SC_CHECK_ABORTF (pth == 0, "pthread_create error %d", pth);
+  }
+
+  SC_INFO ("Main setup waiting\n");
+
+  /* wait until the threads have done their setup */
+  pthread_mutex_lock (&g->mutex);
+  while (g->setup < g->N) {
+    pth = pthread_cond_wait (&g->cond_setup, &g->mutex);
+    SC_CHECK_ABORT (pth == 0, "pthread_cond_wait");
+  }
+  pthread_mutex_unlock (&g->mutex);
+
+  SC_INFO ("Main setup done\n");
+}
+
+static void
+condvar_work (global_data_t * g)
+{
+  int                 i, j;
+  thread_data_t      *td;
+
+  for (j = 0; j < g->T; ++j) {
+
+    /* main thread does some stuff */
+    SC_INFOF ("Main cycle %d start\n", j);
+    pthread_mutex_lock (&g->mutex);
+    SC_ASSERT (g->scheduled == 0 && g->started == 0);
+    pthread_mutex_unlock (&g->mutex);
+
+    for (i = 0; i < g->N; ++i) {
+      /* signal for some task to start working */
+      pthread_mutex_lock (&g->mutex);
+      SC_ASSERT (g->scheduled == i);
+      ++g->scheduled;
+      pthread_mutex_unlock (&g->mutex);
+      pthread_cond_signal (&g->cond_start);
+
+      /* main thread does some stuff */
+    }
+
+    SC_INFOF ("Main cycle %d waiting\n", j);
+    /* main thread does some stuff */
+
+    for (i = g->N - 1; i >= 0; --i) {
+      td = &g->td[i];
+
+      /* main thread waits for tasks to end in reverse order */
+      pthread_mutex_lock (&g->mutex);
+      while (td->done != 1) {
+        pthread_cond_wait (&g->cond_stop, &g->mutex);
+      }
+      SC_ASSERT (td->working == 0);
+      td->done = 0;
+      pthread_mutex_unlock (&g->mutex);
+
+      /* main thread does some stuff */
+    }
+
+    /* main thread does some stuff */
+    SC_INFOF ("Main cycle %d stop\n", j);
+
+    /* reset thread schedule after task is processed */
+    pthread_mutex_lock (&g->mutex);
+    SC_ASSERT (g->scheduled == g->N && g->started == g->N);
+    g->scheduled = g->started = 0;
+    pthread_mutex_unlock (&g->mutex);
+  }
+}
+
+static void
+condvar_teardown (global_data_t * g)
+{
+  int                 i;
+  int                 pth;
+  void               *exitval;
+  thread_data_t      *td;
+
+  SC_INFO ("Main teardown begin\n");
+
+  /* signal all threads to exit */
+  pthread_mutex_lock (&g->mutex);
+  g->scheduled = -1;
+  pthread_mutex_unlock (&g->mutex);
+  pth = pthread_cond_broadcast (&g->cond_start);
+  SC_CHECK_ABORT (pth == 0, "pthread_cond_broadcast");
+
+  SC_INFO ("Main teardown join\n");
+
+  /* wait for all threads to terminate */
+  for (i = 0; i < g->N; ++i) {
+    td = &g->td[i];
+    SC_INFOF ("Main teardown join %02d\n", i);
+    pth = pthread_join (td->thread, &exitval);
+    SC_CHECK_ABORT (pth == 0, "pthread_join");
+    SC_INFOF ("Main teardown done %02d\n", i);
+    SC_ASSERT (exitval == NULL);
+    SC_ASSERT (td->working == 0);
+    SC_ASSERT (td->done == 0);
+  }
+
+  SC_INFO ("Main teardown done\n");
+
+  /* cleanup storage */
+  pthread_attr_destroy (&g->attr);
+  pthread_cond_destroy (&g->cond_stop);
+  pthread_cond_destroy (&g->cond_start);
+  pthread_cond_destroy (&g->cond_setup);
+  pthread_mutex_destroy (&g->mutex);
+  SC_FREE (g->td);
+}
+
+static void
+condvar_run (global_data_t * g)
+{
+  condvar_setup (g);
+  condvar_work (g);
+  condvar_teardown (g);
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 mpithr;
+  int                 first_arg;
+  int                 N, T;
+  sc_options_t       *opt;
+  global_data_t       sg, *g = &sg;
+
+  mpiret = sc_MPI_Init_thread (&argc, &argv, sc_MPI_THREAD_MULTIPLE, &mpithr);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+
+  opt = sc_options_new (argv[0]);
+  sc_options_add_int (opt, 'N', "num-threads", &N, 0, "Number of threads");
+  sc_options_add_int (opt, 'T', "num-tasks", &T, 0, "Number of tasks");
+
+  first_arg = sc_options_parse (sc_package_id, SC_LP_ERROR, opt, argc, argv);
+  if (first_arg != argc || N < 0 || T < 0) {
+    sc_options_print_usage (sc_package_id, SC_LP_ERROR, opt, NULL);
+    sc_abort_collective ("Option parsing failed");
+  }
+  else {
+    sc_options_print_summary (sc_package_id, SC_LP_PRODUCTION, opt);
+  }
+
+  g->N = N;
+  g->T = T;
+  condvar_run (g);
+
+  sc_options_destroy (opt);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/example/pthread/pthread.c b/sc/example/pthread/pthread.c
new file mode 100644
index 0000000..a8f6b18
--- /dev/null
+++ b/sc/example/pthread/pthread.c
@@ -0,0 +1,152 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <pthread.h>
+#include <sc_options.h>
+
+typedef struct thread_data
+{
+  pthread_t           thread;
+  int                 id;
+  MPI_Comm            mpicomm;
+}
+thread_data_t;
+
+static void        *
+start_thread (void *v)
+{
+  thread_data_t      *td = (thread_data_t *) v;
+  int                 j;
+  int                *p;
+  int                 mpiret;
+  int                 mpisize;
+
+  /* randomize thread startup time */
+  sleep (4. * rand () / (RAND_MAX + 1.));
+  SC_INFOF ("This is thread %d\n", td->id);
+
+  /* create some data */
+  p = SC_ALLOC (int, 1000);
+  for (j = 0; j < 1000; ++j) {
+    p[j] = j + 17 * td->id;
+  }
+
+  /* duplicate communicator and execute a collective MPI call */
+  mpiret = sc_MPI_Comm_size (td->mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Allreduce (p, p + 500, 500, sc_MPI_INT, sc_MPI_SUM,
+                             td->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  /* check the results and free data */
+  for (j = 500; j < 1000; ++j) {
+    SC_CHECK_ABORT (p[j] == (j - 500 + 17 * td->id) * mpisize,
+                    "Communication mismatch");
+  }
+  SC_FREE (p);
+
+  /* automatically calls pthread_exit (v) */
+  return v;
+}
+
+static void
+test_threads (int N)
+{
+  int                 mpiret;
+  int                 i;
+  int                 pth;
+  void               *exitval;
+  pthread_attr_t      attr;
+  thread_data_t      *td;
+
+  /* allocate thread data */
+  td = SC_ALLOC (thread_data_t, N);
+
+  /* create and run threads */
+  pth = pthread_attr_init (&attr);
+  SC_CHECK_ABORT (pth == 0, "Fail in pthread_attr_init");
+  pth = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE);
+  SC_CHECK_ABORT (pth == 0, "Fail in pthread_attr_setdetachstate");
+  for (i = 0; i < N; ++i) {
+    mpiret = sc_MPI_Comm_dup (sc_MPI_COMM_WORLD, &td[i].mpicomm);
+    SC_CHECK_MPI (mpiret);
+    td[i].id = i;
+    pth = pthread_create (&td[i].thread, &attr, &start_thread, &td[i]);
+    SC_CHECK_ABORTF (pth == 0, "pthread_create error %d", pth);
+  }
+
+  /* wait for threads to finish */
+  for (i = 0; i < N; ++i) {
+    pth = pthread_join (td[i].thread, &exitval);
+    SC_CHECK_ABORT (pth == 0, "Fail in pthread_join");
+    SC_ASSERT (exitval == &td[i]);
+    mpiret = sc_MPI_Comm_free (&td[i].mpicomm);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  /* destroy attribute and thread data */
+  pth = pthread_attr_destroy (&attr);
+  SC_CHECK_ABORT (pth == 0, "Fail in pthread_attr_destroy");
+  SC_FREE (td);
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 mpithr;
+  int                 first_arg;
+  int                 N;
+  sc_options_t       *opt;
+
+  mpiret = sc_MPI_Init_thread (&argc, &argv, sc_MPI_THREAD_MULTIPLE, &mpithr);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+
+  opt = sc_options_new (argv[0]);
+  sc_options_add_int (opt, 'N', "num-threads", &N, 0, "Number of threads");
+
+  first_arg = sc_options_parse (sc_package_id, SC_LP_ERROR, opt, argc, argv);
+  if (first_arg != argc || N < 0) {
+    sc_options_print_usage (sc_package_id, SC_LP_ERROR, opt, NULL);
+    sc_abort_collective ("Option parsing failed");
+  }
+  else {
+    sc_options_print_summary (sc_package_id, SC_LP_PRODUCTION, opt);
+  }
+
+  if (mpithr < sc_MPI_THREAD_MULTIPLE) {
+    SC_GLOBAL_PRODUCTIONF ("MPI thread support is only %d\n", mpithr);
+  }
+  else {
+    test_threads (N);
+  }
+
+  sc_options_destroy (opt);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/example/warp/Makefile.am b/sc/example/warp/Makefile.am
new file mode 100644
index 0000000..5cd4239
--- /dev/null
+++ b/sc/example/warp/Makefile.am
@@ -0,0 +1,9 @@
+
+# This file is part of the SC Library
+# Makefile.am in example/warp
+# included non-recursively from toplevel directory
+
+bin_PROGRAMS += example/warp/sc_warp
+example_warp_sc_warp_SOURCES = example/warp/warp.c
+
+LINT_CSOURCES += $(example_warp_sc_warp_SOURCES)
diff --git a/sc/example/warp/warp.c b/sc/example/warp/warp.c
new file mode 100644
index 0000000..6fbba4b
--- /dev/null
+++ b/sc/example/warp/warp.c
@@ -0,0 +1,53 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_warp.h>
+
+int
+main (void)
+{
+  double              round1[3];
+  double              round2[3];
+  sc_warp_interval_t *root;
+
+  sc_init (sc_MPI_COMM_NULL, 1, 1, NULL, SC_LP_DEFAULT);
+
+  root = sc_warp_new (0., 1.);
+
+  round1[0] = .3;
+  round1[1] = .58;
+  round1[2] = .86;
+  sc_warp_update (root, 3, round1, 0.10, 7);
+  sc_warp_write (root, stdout);
+
+  round2[0] = .3;
+  round2[1] = .86;
+  round2[2] = .92;
+  sc_warp_update (root, 3, round2, 0.15, 7);
+  sc_warp_write (root, stdout);
+
+  sc_warp_destroy (root);
+
+  sc_finalize ();
+
+  return 0;
+}
diff --git a/sc/iniparser/AUTHORS b/sc/iniparser/AUTHORS
new file mode 100644
index 0000000..b44d8cb
--- /dev/null
+++ b/sc/iniparser/AUTHORS
@@ -0,0 +1,5 @@
+Author: Nicolas Devillard <ndevilla at free.fr>
+
+This tiny library has received countless contributions and I have
+not kept track of all the people who contributed. Let them be thanked
+for their ideas, code, suggestions, corrections, enhancements!
diff --git a/sc/iniparser/LICENSE b/sc/iniparser/LICENSE
new file mode 100644
index 0000000..5a3a80b
--- /dev/null
+++ b/sc/iniparser/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2000-2011 by Nicolas Devillard.
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
diff --git a/sc/iniparser/Makefile.am b/sc/iniparser/Makefile.am
new file mode 100644
index 0000000..635f943
--- /dev/null
+++ b/sc/iniparser/Makefile.am
@@ -0,0 +1,21 @@
+
+# This file is part of the SC Library
+# Makefile.am in iniparser
+# included non-recursively from toplevel directory
+
+noinst_PROGRAMS += iniparser/sc_iniexample
+iniparser_sc_iniexample_SOURCES = iniparser/iniexample.c
+iniparser_sc_iniexample_CPPFLAGS = $(AM_CPPFLAGS) -I at top_srcdir@/iniparser
+
+iniparser_internal_headers = iniparser/dictionary.h iniparser/iniparser.h
+iniparser_compiled_sources = iniparser/dictionary.c iniparser/iniparser.c
+
+libsc_internal_headers += $(iniparser_internal_headers)
+libsc_compiled_sources += $(iniparser_compiled_sources)
+LIBSC_CPPFLAGS += -I at top_srcdir@/iniparser
+
+EXTRA_DIST += iniparser/AUTHORS iniparser/LICENSE
+dist_scini_DATA += iniparser/twisted.ini
+
+LINT_CSOURCES += \
+	$(iniparser_sc_iniexample_SOURCES)
diff --git a/sc/iniparser/README b/sc/iniparser/README
new file mode 100644
index 0000000..bc69787
--- /dev/null
+++ b/sc/iniparser/README
@@ -0,0 +1,12 @@
+
+Welcome to iniParser -- version 3.1
+released 08 Apr 2012
+
+This modules offers parsing of ini files from the C level.
+See a complete documentation in HTML format, from this directory
+open the file html/index.html with any HTML-capable browser.
+
+Enjoy!
+
+N.Devillard
+Sun Apr  8 16:38:09 CEST 2012
diff --git a/sc/iniparser/dictionary.c b/sc/iniparser/dictionary.c
new file mode 100644
index 0000000..3f0f5cf
--- /dev/null
+++ b/sc/iniparser/dictionary.c
@@ -0,0 +1,398 @@
+/*-------------------------------------------------------------------------*/
+/**
+   @file    dictionary.c
+   @author  N. Devillard
+   @brief   Implements a dictionary for string variables.
+
+   This module implements a simple dictionary object, i.e. a list
+   of string/string associations. This object is useful to store e.g.
+   informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+#include "dictionary.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/** Maximum value size for integers and doubles. */
+#define MAXVALSZ    1024
+
+/** Minimal allocated number of entries in a dictionary */
+#define DICTMINSZ   128
+
+/** Invalid key token */
+#define DICT_INVALID_KEY    ((char*)-1)
+
+/*---------------------------------------------------------------------------
+                            Private functions
+ ---------------------------------------------------------------------------*/
+
+/* Doubles the allocated size associated to a pointer */
+/* 'size' is the current allocated size. */
+static void * mem_double(void * ptr, int size)
+{
+    void * newptr ;
+ 
+    newptr = calloc(2*size, 1);
+    if (newptr==NULL) {
+        return NULL ;
+    }
+    memcpy(newptr, ptr, size);
+    free(ptr);
+    return newptr ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Duplicate a string
+  @param    s String to duplicate
+  @return   Pointer to a newly allocated string, to be freed with free()
+
+  This is a replacement for strdup(). This implementation is provided
+  for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+static char * xstrdup(const char * s)
+{
+    char * t ;
+    if (!s)
+        return NULL ;
+    t = (char*)malloc(strlen(s)+1) ;
+    if (t) {
+        strcpy(t,s);
+    }
+    return t ;
+}
+
+/*---------------------------------------------------------------------------
+                            Function codes
+ ---------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Compute the hash key for a string.
+  @param    key     Character string to use for key.
+  @return   1 unsigned int on at least 32 bits.
+
+  This hash function has been taken from an Article in Dr Dobbs Journal.
+  This is normally a collision-free function, distributing keys evenly.
+  The key is stored anyway in the struct so that collision can be avoided
+  by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key)
+{
+    int         len ;
+    unsigned    hash ;
+    int         i ;
+
+    len = strlen(key);
+    for (hash=0, i=0 ; i<len ; i++) {
+        hash += (unsigned)key[i] ;
+        hash += (hash<<10);
+        hash ^= (hash>>6) ;
+    }
+    hash += (hash <<3);
+    hash ^= (hash >>11);
+    hash += (hash <<15);
+    return hash ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Create a new dictionary object.
+  @param    size    Optional initial size of the dictionary.
+  @return   1 newly allocated dictionary objet.
+
+  This function allocates a new dictionary object of given size and returns
+  it. If you do not know in advance (roughly) the number of entries in the
+  dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(int size)
+{
+    dictionary  *   d ;
+
+    /* If no size was specified, allocate space for DICTMINSZ */
+    if (size<DICTMINSZ) size=DICTMINSZ ;
+
+    if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) {
+        return NULL;
+    }
+    d->size = size ;
+    d->val  = (char **)calloc(size, sizeof(char*));
+    d->key  = (char **)calloc(size, sizeof(char*));
+    d->hash = (unsigned int *)calloc(size, sizeof(unsigned));
+    return d ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a dictionary object
+  @param    d   dictionary object to deallocate.
+  @return   void
+
+  Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * d)
+{
+    int     i ;
+
+    if (d==NULL) return ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]!=NULL)
+            free(d->key[i]);
+        if (d->val[i]!=NULL)
+            free(d->val[i]);
+    }
+    free(d->val);
+    free(d->key);
+    free(d->hash);
+    free(d);
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get a value from a dictionary.
+  @param    d       dictionary object to search.
+  @param    key     Key to look for in the dictionary.
+  @param    def     Default value to return if key not found.
+  @return   1 pointer to internally allocated character string.
+
+  This function locates a key in a dictionary and returns a pointer to its
+  value, or the passed 'def' pointer if no such key can be found in
+  dictionary. The returned character pointer points to data internal to the
+  dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def)
+{
+    unsigned    hash ;
+    int         i ;
+
+    hash = dictionary_hash(key);
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        /* Compare hash */
+        if (hash==d->hash[i]) {
+            /* Compare string, to avoid hash collisions */
+            if (!strcmp(key, d->key[i])) {
+                return d->val[i] ;
+            }
+        }
+    }
+    return def ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set a value in a dictionary.
+  @param    d       dictionary object to modify.
+  @param    key     Key to modify or add.
+  @param    val     Value to add.
+  @return   int     0 if Ok, anything else otherwise
+
+  If the given key is found in the dictionary, the associated value is
+  replaced by the provided one. If the key cannot be found in the
+  dictionary, it is added to it.
+
+  It is Ok to provide a NULL value for val, but NULL values for the dictionary
+  or the key are considered as errors: the function will return immediately
+  in such a case.
+
+  Notice that if you dictionary_set a variable to NULL, a call to
+  dictionary_get will return a NULL value: the variable will be found, and
+  its value (NULL) is returned. In other words, setting the variable
+  content to NULL is equivalent to deleting the variable from the
+  dictionary. It is not possible (in this implementation) to have a key in
+  the dictionary without value.
+
+  This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * d, const char * key, const char * val)
+{
+    int         i ;
+    unsigned    hash ;
+
+    if (d==NULL || key==NULL) return -1 ;
+    
+    /* Compute hash for this key */
+    hash = dictionary_hash(key) ;
+    /* Find if value is already in dictionary */
+    if (d->n>0) {
+        for (i=0 ; i<d->size ; i++) {
+            if (d->key[i]==NULL)
+                continue ;
+            if (hash==d->hash[i]) { /* Same hash value */
+                if (!strcmp(key, d->key[i])) {   /* Same key */
+                    /* Found a value: modify and return */
+                    if (d->val[i]!=NULL)
+                        free(d->val[i]);
+                    d->val[i] = val ? xstrdup(val) : NULL ;
+                    /* Value has been modified: return */
+                    return 0 ;
+                }
+            }
+        }
+    }
+    /* Add a new value */
+    /* See if dictionary needs to grow */
+    if (d->n==d->size) {
+
+        /* Reached maximum size: reallocate dictionary */
+        d->val  = (char **)mem_double(d->val,  d->size * sizeof(char*)) ;
+        d->key  = (char **)mem_double(d->key,  d->size * sizeof(char*)) ;
+        d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ;
+        if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) {
+            /* Cannot grow dictionary */
+            return -1 ;
+        }
+        /* Double size */
+        d->size *= 2 ;
+    }
+
+    /* Insert key in the first empty slot. Start at d->n and wrap at
+       d->size. Because d->n < d->size this will necessarily
+       terminate. */
+    for (i=d->n ; d->key[i] ; ) {
+        if(++i == d->size) i = 0;
+    }
+    /* Copy key */
+    d->key[i]  = xstrdup(key);
+    d->val[i]  = val ? xstrdup(val) : NULL ;
+    d->hash[i] = hash;
+    d->n ++ ;
+    return 0 ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a key in a dictionary
+  @param    d       dictionary object to modify.
+  @param    key     Key to remove.
+  @return   void
+
+  This function deletes a key in a dictionary. Nothing is done if the
+  key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key)
+{
+    unsigned    hash ;
+    int         i ;
+
+    if (key == NULL) {
+        return;
+    }
+
+    hash = dictionary_hash(key);
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        /* Compare hash */
+        if (hash==d->hash[i]) {
+            /* Compare string, to avoid hash collisions */
+            if (!strcmp(key, d->key[i])) {
+                /* Found key */
+                break ;
+            }
+        }
+    }
+    if (i>=d->size)
+        /* Key not found */
+        return ;
+
+    free(d->key[i]);
+    d->key[i] = NULL ;
+    if (d->val[i]!=NULL) {
+        free(d->val[i]);
+        d->val[i] = NULL ;
+    }
+    d->hash[i] = 0 ;
+    d->n -- ;
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer.
+  @return   void
+
+  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+  output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out)
+{
+    int     i ;
+
+    if (d==NULL || out==NULL) return ;
+    if (d->n<1) {
+        fprintf(out, "empty dictionary\n");
+        return ;
+    }
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]) {
+            fprintf(out, "%20s\t[%s]\n",
+                    d->key[i],
+                    d->val[i] ? d->val[i] : "UNDEF");
+        }
+    }
+    return ;
+}
+
+
+/* Test code */
+#ifdef TESTDIC
+#define NVALS 20000
+int main(int argc, char *argv[])
+{
+    dictionary  *   d ;
+    char    *   val ;
+    int         i ;
+    char        cval[90] ;
+
+    /* Allocate dictionary */
+    printf("allocating...\n");
+    d = dictionary_new(0);
+    
+    /* Set values in dictionary */
+    printf("setting %d values...\n", NVALS);
+    for (i=0 ; i<NVALS ; i++) {
+        sprintf(cval, "%04d", i);
+        dictionary_set(d, cval, "salut");
+    }
+    printf("getting %d values...\n", NVALS);
+    for (i=0 ; i<NVALS ; i++) {
+        sprintf(cval, "%04d", i);
+        val = dictionary_get(d, cval, DICT_INVALID_KEY);
+        if (val==DICT_INVALID_KEY) {
+            printf("cannot get value for key [%s]\n", cval);
+        }
+    }
+    printf("unsetting %d values...\n", NVALS);
+    for (i=0 ; i<NVALS ; i++) {
+        sprintf(cval, "%04d", i);
+        dictionary_unset(d, cval);
+    }
+    if (d->n != 0) {
+        printf("error deleting values\n");
+    }
+    printf("deallocating...\n");
+    dictionary_del(d);
+    return 0 ;
+}
+#endif
+/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/sc/iniparser/dictionary.h b/sc/iniparser/dictionary.h
new file mode 100644
index 0000000..e3a4cef
--- /dev/null
+++ b/sc/iniparser/dictionary.h
@@ -0,0 +1,187 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    dictionary.h
+   @author  N. Devillard
+   @brief   Implements a dictionary for string variables.
+
+   This module implements a simple dictionary object, i.e. a list
+   of string/string associations. This object is useful to store e.g.
+   informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _DICTIONARY_H_
+#define _DICTIONARY_H_
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sc_config.h>
+#ifdef SC_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/*---------------------------------------------------------------------------
+                                Protect C++ compilation
+ ---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
+/*---------------------------------------------------------------------------
+                                New types
+ ---------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dictionary object
+
+  This object contains a list of string/string associations. Each
+  association is identified by a unique string key. Looking up values
+  in the dictionary is speeded up by the use of a (hopefully collision-free)
+  hash function.
+ */
+/*-------------------------------------------------------------------------*/
+typedef struct _dictionary_ {
+    int             n ;     /** Number of entries in dictionary */
+    int             size ;  /** Storage size */
+    char        **  val ;   /** List of string values */
+    char        **  key ;   /** List of string keys */
+    unsigned     *  hash ;  /** List of hash values for keys */
+} dictionary ;
+
+
+/*---------------------------------------------------------------------------
+                            Function prototypes
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Compute the hash key for a string.
+  @param    key     Character string to use for key.
+  @return   1 unsigned int on at least 32 bits.
+
+  This hash function has been taken from an Article in Dr Dobbs Journal.
+  This is normally a collision-free function, distributing keys evenly.
+  The key is stored anyway in the struct so that collision can be avoided
+  by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Create a new dictionary object.
+  @param    size    Optional initial size of the dictionary.
+  @return   1 newly allocated dictionary objet.
+
+  This function allocates a new dictionary object of given size and returns
+  it. If you do not know in advance (roughly) the number of entries in the
+  dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(int size);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a dictionary object
+  @param    d   dictionary object to deallocate.
+  @return   void
+
+  Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * vd);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get a value from a dictionary.
+  @param    d       dictionary object to search.
+  @param    key     Key to look for in the dictionary.
+  @param    def     Default value to return if key not found.
+  @return   1 pointer to internally allocated character string.
+
+  This function locates a key in a dictionary and returns a pointer to its
+  value, or the passed 'def' pointer if no such key can be found in
+  dictionary. The returned character pointer points to data internal to the
+  dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set a value in a dictionary.
+  @param    d       dictionary object to modify.
+  @param    key     Key to modify or add.
+  @param    val     Value to add.
+  @return   int     0 if Ok, anything else otherwise
+
+  If the given key is found in the dictionary, the associated value is
+  replaced by the provided one. If the key cannot be found in the
+  dictionary, it is added to it.
+
+  It is Ok to provide a NULL value for val, but NULL values for the dictionary
+  or the key are considered as errors: the function will return immediately
+  in such a case.
+
+  Notice that if you dictionary_set a variable to NULL, a call to
+  dictionary_get will return a NULL value: the variable will be found, and
+  its value (NULL) is returned. In other words, setting the variable
+  content to NULL is equivalent to deleting the variable from the
+  dictionary. It is not possible (in this implementation) to have a key in
+  the dictionary without value.
+
+  This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * vd, const char * key, const char * val);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a key in a dictionary
+  @param    d       dictionary object to modify.
+  @param    key     Key to remove.
+  @return   void
+
+  This function deletes a key in a dictionary. Nothing is done if the
+  key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer.
+  @return   void
+
+  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+  output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out);
+
+#ifdef __cplusplus
+#if 0
+{
+#endif
+}
+#endif
+
+#endif
diff --git a/sc/iniparser/iniexample.c b/sc/iniparser/iniexample.c
new file mode 100644
index 0000000..b651a6f
--- /dev/null
+++ b/sc/iniparser/iniexample.c
@@ -0,0 +1,100 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "iniparser.h"
+
+static void create_example_ini_file(void);
+static int  parse_ini_file(const char * ini_name);
+
+int main(int argc, char * argv[])
+{
+    int     status ;
+
+    if (argc<2) {
+        create_example_ini_file();
+        status = parse_ini_file("example.ini");
+    } else {
+        status = parse_ini_file(argv[1]);
+    }
+    return status ;
+}
+
+static void create_example_ini_file(void)
+{
+    FILE    *   ini ;
+
+    ini = fopen("example.ini", "w");
+    fprintf(ini,
+    "#\n"
+    "# This is an example of ini file\n"
+    "#\n"
+    "\n"
+    "[Pizza]\n"
+    "\n"
+    "Ham       = yes ;\n"
+    "Mushrooms = TRUE ;\n"
+    "Capres    = 0 ;\n"
+    "Cheese    = Non ;\n"
+    "\n"
+    "\n"
+    "[Wine]\n"
+    "\n"
+    "Grape     = Cabernet Sauvignon ;\n"
+    "Year      = 1989 ;\n"
+    "Country   = Spain ;\n"
+    "Alcohol   = 12.5  ;\n"
+    "\n");
+    fclose(ini);
+}
+
+
+static int parse_ini_file(const char * ini_name)
+{
+    dictionary  *   ini ;
+
+    /* Some temporary variables to hold query results */
+    int             b ;
+    int             i ;
+    double          d ;
+    char        *   s ;
+
+    ini = iniparser_load(ini_name);
+    if (ini==NULL) {
+        fprintf(stderr, "cannot parse file: %s\n", ini_name);
+        return -1 ;
+    }
+    iniparser_dump(ini, stderr);
+
+    /* Get pizza attributes */
+    printf("Pizza:\n");
+
+    b = iniparser_getboolean(ini, "pizza:ham", -1);
+    printf("Ham:       [%d]\n", b);
+    b = iniparser_getboolean(ini, "pizza:mushrooms", -1);
+    printf("Mushrooms: [%d]\n", b);
+    b = iniparser_getboolean(ini, "pizza:capres", -1);
+    printf("Capres:    [%d]\n", b);
+    b = iniparser_getboolean(ini, "pizza:cheese", -1);
+    printf("Cheese:    [%d]\n", b);
+
+    /* Get wine attributes */
+    printf("Wine:\n");
+    s = iniparser_getstring(ini, "wine:grape", NULL);
+    printf("Grape:     [%s]\n", s ? s : "UNDEF");
+    
+    i = iniparser_getint(ini, "wine:year", -1);
+    printf("Year:      [%d]\n", i);
+
+    s = iniparser_getstring(ini, "wine:country", NULL);
+    printf("Country:   [%s]\n", s ? s : "UNDEF");
+    
+    d = iniparser_getdouble(ini, "wine:alcohol", -1.0);
+    printf("Alcohol:   [%g]\n", d);
+
+    iniparser_freedict(ini);
+    return 0 ;
+}
+
+
diff --git a/sc/iniparser/iniparser.c b/sc/iniparser/iniparser.c
new file mode 100644
index 0000000..d557955
--- /dev/null
+++ b/sc/iniparser/iniparser.c
@@ -0,0 +1,748 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    iniparser.c
+   @author  N. Devillard
+   @brief   Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+/*---------------------------- Includes ------------------------------------*/
+#include <ctype.h>
+#include "iniparser.h"
+
+/*---------------------------- Defines -------------------------------------*/
+#define ASCIILINESZ         (1024)
+#define INI_INVALID_KEY     ((char*)-1)
+
+/*---------------------------------------------------------------------------
+                        Private to this module
+ ---------------------------------------------------------------------------*/
+/**
+ * This enum stores the status for each parsed line (internal use only).
+ */
+typedef enum _line_status_ {
+    LINE_UNPROCESSED,
+    LINE_ERROR,
+    LINE_EMPTY,
+    LINE_COMMENT,
+    LINE_SECTION,
+    LINE_VALUE
+} line_status ;
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Convert a string to lowercase.
+  @param    s   String to convert.
+  @return   ptr to statically allocated string.
+
+  This function returns a pointer to a statically allocated string
+  containing a lowercased version of the input string. Do not free
+  or modify the returned string! Since the returned string is statically
+  allocated, it will be modified at each function call (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strlwc(const char * s)
+{
+    static char l[ASCIILINESZ+1];
+    int i ;
+
+    if (s==NULL) return NULL ;
+    memset(l, 0, ASCIILINESZ+1);
+    i=0 ;
+    while (s[i] && i<ASCIILINESZ) {
+        l[i] = (char)tolower((int)s[i]);
+        i++ ;
+    }
+    l[ASCIILINESZ]=(char)0;
+    return l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Remove blanks at the beginning and the end of a string.
+  @param    s   String to parse.
+  @return   ptr to statically allocated string.
+
+  This function returns a pointer to a statically allocated string,
+  which is identical to the input string, except that all blank
+  characters at the end and the beg. of the string have been removed.
+  Do not free or modify the returned string! Since the returned string
+  is statically allocated, it will be modified at each function call
+  (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strstrip(const char * s)
+{
+    static char l[ASCIILINESZ+1];
+    char * last ;
+    
+    if (s==NULL) return NULL ;
+    
+    while (isspace((int)*s) && *s) s++;
+    memset(l, 0, ASCIILINESZ+1);
+    strcpy(l, s);
+    last = l + strlen(l);
+    while (last > l) {
+        if (!isspace((int)*(last-1)))
+            break ;
+        last -- ;
+    }
+    *last = (char)0;
+    return (char*)l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get number of sections in a dictionary
+  @param    d   Dictionary to examine
+  @return   int Number of sections found in dictionary
+
+  This function returns the number of sections found in a dictionary.
+  The test to recognize sections is done on the string stored in the
+  dictionary: a section name is given as "section" whereas a key is
+  stored as "section:key", thus the test looks for entries that do not
+  contain a colon.
+
+  This clearly fails in the case a section name contains a colon, but
+  this should simply be avoided.
+
+  This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getnsec(dictionary * d)
+{
+    int i ;
+    int nsec ;
+
+    if (d==NULL) return -1 ;
+    nsec=0 ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (strchr(d->key[i], ':')==NULL) {
+            nsec ++ ;
+        }
+    }
+    return nsec ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get name for section n in a dictionary.
+  @param    d   Dictionary to examine
+  @param    n   Section number (from 0 to nsec-1).
+  @return   Pointer to char string
+
+  This function locates the n-th section in a dictionary and returns
+  its name as a pointer to a string statically allocated inside the
+  dictionary. Do not free or modify the returned string!
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getsecname(dictionary * d, int n)
+{
+    int i ;
+    int foundsec ;
+
+    if (d==NULL || n<0) return NULL ;
+    foundsec=0 ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (strchr(d->key[i], ':')==NULL) {
+            foundsec++ ;
+            if (foundsec>n)
+                break ;
+        }
+    }
+    if (foundsec<=n) {
+        return NULL ;
+    }
+    return d->key[i] ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump.
+  @param    f   Opened file pointer to dump to.
+  @return   void
+
+  This function prints out the contents of a dictionary, one element by
+  line, onto the provided file pointer. It is OK to specify @c stderr
+  or @c stdout as output files. This function is meant for debugging
+  purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f)
+{
+    int     i ;
+
+    if (d==NULL || f==NULL) return ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (d->val[i]!=NULL) {
+            fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
+        } else {
+            fprintf(f, "[%s]=UNDEF\n", d->key[i]);
+        }
+    }
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given dictionary into a loadable ini file.
+  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump_ini(dictionary * d, FILE * f)
+{
+    int     i ;
+    int     nsec ;
+    char *  secname ;
+
+    if (d==NULL || f==NULL) return ;
+
+    nsec = iniparser_getnsec(d);
+    if (nsec<1) {
+        /* No section in file: dump all keys as they are */
+        for (i=0 ; i<d->size ; i++) {
+            if (d->key[i]==NULL)
+                continue ;
+            fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
+        }
+        return ;
+    }
+    for (i=0 ; i<nsec ; i++) {
+        secname = iniparser_getsecname(d, i) ;
+        iniparser_dumpsection_ini(d, secname, f) ;
+    }
+    fprintf(f, "\n");
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary section to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    s   Section name of dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given section of a given dictionary into a loadable ini
+  file.  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f)
+{
+    int     j ;
+    char    keym[ASCIILINESZ+1];
+    int     seclen ;
+
+    if (d==NULL || f==NULL) return ;
+    if (! iniparser_find_entry(d, s)) return ;
+
+    seclen  = (int)strlen(s);
+    fprintf(f, "\n[%s]\n", s);
+    sprintf(keym, "%s:", s);
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1)) {
+            fprintf(f,
+                    "%-30s = %s\n",
+                    d->key[j]+seclen+1,
+                    d->val[j] ? d->val[j] : "");
+        }
+    }
+    fprintf(f, "\n");
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s)
+{
+    int     seclen, nkeys ;
+    char    keym[ASCIILINESZ+1];
+    int j ;
+
+    nkeys = 0;
+
+    if (d==NULL) return nkeys;
+    if (! iniparser_find_entry(d, s)) return nkeys;
+
+    seclen  = (int)strlen(s);
+    sprintf(keym, "%s:", s);
+
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1)) 
+            nkeys++;
+    }
+
+    return nkeys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   pointer to statically allocated character strings
+
+  This function queries a dictionary and finds all keys in a given section.
+  Each pointer in the returned char pointer-to-pointer is pointing to
+  a string allocated in the dictionary; do not free or modify them.
+  
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s)
+{
+
+    char **keys;
+
+    int i, j ;
+    char    keym[ASCIILINESZ+1];
+    int     seclen, nkeys ;
+
+    keys = NULL;
+
+    if (d==NULL) return keys;
+    if (! iniparser_find_entry(d, s)) return keys;
+
+    nkeys = iniparser_getsecnkeys(d, s);
+
+    keys = (char**) malloc(nkeys*sizeof(char*));
+
+    seclen  = (int)strlen(s);
+    sprintf(keym, "%s:", s);
+    
+    i = 0;
+
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1)) {
+            keys[i] = d->key[j];
+            i++;
+        }
+    }
+
+    return keys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key
+  @param    d       Dictionary to search
+  @param    key     Key string to look for
+  @param    def     Default value to return if key not found.
+  @return   pointer to statically allocated character string
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the pointer passed as 'def' is returned.
+  The returned char pointer is pointing to a string allocated in
+  the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def)
+{
+    char * lc_key ;
+    char * sval ;
+
+    if (d==NULL || key==NULL)
+        return def ;
+
+    lc_key = strlwc(key);
+    sval = dictionary_get(d, lc_key, def);
+    return sval ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to an int
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  Supported values for integers include the usual C notation
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+  are supported. Examples:
+
+  "42"      ->  42
+  "042"     ->  34 (octal -> decimal)
+  "0x42"    ->  66 (hexa  -> decimal)
+
+  Warning: the conversion may overflow in various ways. Conversion is
+  totally outsourced to strtol(), see the associated man page for overflow
+  handling.
+
+  Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound)
+{
+    char    *   str ;
+
+    str = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (str==INI_INVALID_KEY) return notfound ;
+    return (int)strtol(str, NULL, 0);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a double
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   double
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound)
+{
+    char    *   str ;
+
+    str = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (str==INI_INVALID_KEY) return notfound ;
+    return atof(str);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a boolean
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  A true boolean is found if one of the following is matched:
+
+  - A string starting with 'y'
+  - A string starting with 'Y'
+  - A string starting with 't'
+  - A string starting with 'T'
+  - A string starting with '1'
+
+  A false boolean is found if one of the following is matched:
+
+  - A string starting with 'n'
+  - A string starting with 'N'
+  - A string starting with 'f'
+  - A string starting with 'F'
+  - A string starting with '0'
+
+  The notfound value returned if no boolean is identified, does not
+  necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound)
+{
+    char    *   c ;
+    int         ret ;
+
+    c = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (c==INI_INVALID_KEY) return notfound ;
+    if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
+        ret = 1 ;
+    } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
+        ret = 0 ;
+    } else {
+        ret = notfound ;
+    }
+    return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Finds out if a given entry exists in a dictionary
+  @param    ini     Dictionary to search
+  @param    entry   Name of the entry to look for
+  @return   integer 1 if entry exists, 0 otherwise
+
+  Finds out if a given entry exists in the dictionary. Since sections
+  are stored as keys with NULL associated values, this is the only way
+  of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(
+    dictionary  *   ini,
+    const char  *   entry
+)
+{
+    int found=0 ;
+    if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
+        found = 1 ;
+    }
+    return found ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set an entry in a dictionary.
+  @param    ini     Dictionary to modify.
+  @param    entry   Entry to modify (entry name)
+  @param    val     New value to associate to the entry.
+  @return   int 0 if Ok, -1 otherwise.
+
+  If the given entry can be found in the dictionary, it is modified to
+  contain the provided value. If it cannot be found, -1 is returned.
+  It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val)
+{
+    return dictionary_set(ini, strlwc(entry), val) ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete an entry in a dictionary
+  @param    ini     Dictionary to modify
+  @param    entry   Entry to delete (entry name)
+  @return   void
+
+  If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry)
+{
+    dictionary_unset(ini, strlwc(entry));
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Load a single line from an INI file
+  @param    input_line  Input line, may be concatenated multi-line input
+  @param    section     Output space to store section
+  @param    key         Output space to store key
+  @param    value       Output space to store value
+  @return   line_status value
+ */
+/*--------------------------------------------------------------------------*/
+static line_status iniparser_line(
+    const char * input_line,
+    char * section,
+    char * key,
+    char * value)
+{   
+    line_status sta ;
+    char        line[ASCIILINESZ+1];
+    int         len ;
+
+    strcpy(line, strstrip(input_line));
+    len = (int)strlen(line);
+
+    sta = LINE_UNPROCESSED ;
+    if (len<1) {
+        /* Empty line */
+        sta = LINE_EMPTY ;
+    } else if (line[0]=='#' || line[0]==';') {
+        /* Comment line */
+        sta = LINE_COMMENT ; 
+    } else if (line[0]=='[' && line[len-1]==']') {
+        /* Section name */
+        sscanf(line, "[%[^]]", section);
+        strcpy(section, strstrip(section));
+        strcpy(section, strlwc(section));
+        sta = LINE_SECTION ;
+    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
+           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
+           ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) {
+        /* Usual key=value, with or without comments */
+        strcpy(key, strstrip(key));
+        strcpy(key, strlwc(key));
+        strcpy(value, strstrip(value));
+        /*
+         * sscanf cannot handle '' or "" as empty values
+         * this is done here
+         */
+        if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
+            value[0]=0 ;
+        }
+        sta = LINE_VALUE ;
+    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
+           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
+        /*
+         * Special cases:
+         * key=
+         * key=;
+         * key=#
+         */
+        strcpy(key, strstrip(key));
+        strcpy(key, strlwc(key));
+        value[0]=0 ;
+        sta = LINE_VALUE ;
+    } else {
+        /* Generate syntax error */
+        sta = LINE_ERROR ;
+    }
+    return sta ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Parse an ini file and return an allocated dictionary object
+  @param    ininame Name of the ini file to read.
+  @return   Pointer to newly allocated dictionary
+
+  This is the parser for ini files. This function is called, providing
+  the name of the file to be read. It returns a dictionary object that
+  should not be accessed directly, but through accessor functions
+  instead.
+
+  The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame)
+{
+    FILE * in ;
+
+    char line    [ASCIILINESZ+1] ;
+    char section [ASCIILINESZ+1] ;
+    char key     [ASCIILINESZ+1] ;
+    char tmp     [ASCIILINESZ+1] ;
+    char val     [ASCIILINESZ+1] ;
+
+    int  last=0 ;
+    int  len ;
+    int  lineno=0 ;
+    int  errs=0;
+
+    dictionary * dict ;
+
+    if ((in=fopen(ininame, "r"))==NULL) {
+        fprintf(stderr, "iniparser: cannot open %s\n", ininame);
+        return NULL ;
+    }
+
+    dict = dictionary_new(0) ;
+    if (!dict) {
+        fclose(in);
+        return NULL ;
+    }
+
+    memset(line,    0, ASCIILINESZ);
+    memset(section, 0, ASCIILINESZ);
+    memset(key,     0, ASCIILINESZ);
+    memset(val,     0, ASCIILINESZ);
+    last=0 ;
+
+    while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
+        lineno++ ;
+        len = (int)strlen(line)-1;
+        if (len==0)
+            continue;
+        /* Safety check against buffer overflows */
+        if (line[len]!='\n') {
+            fprintf(stderr,
+                    "iniparser: input line too long in %s (%d)\n",
+                    ininame,
+                    lineno);
+            dictionary_del(dict);
+            fclose(in);
+            return NULL ;
+        }
+        /* Get rid of \n and spaces at end of line */
+        while ((len>=0) &&
+                ((line[len]=='\n') || (isspace(line[len])))) {
+            line[len]=0 ;
+            len-- ;
+        }
+        /* Detect multi-line */
+        if (line[len]=='\\') {
+            /* Multi-line value */
+            last=len ;
+            continue ;
+        } else {
+            last=0 ;
+        }
+        switch (iniparser_line(line, section, key, val)) {
+            case LINE_EMPTY:
+            case LINE_COMMENT:
+            break ;
+
+            case LINE_SECTION:
+            errs = dictionary_set(dict, section, NULL);
+            break ;
+
+            case LINE_VALUE:
+            sprintf(tmp, "%s:%s", section, key);
+            errs = dictionary_set(dict, tmp, val) ;
+            break ;
+
+            case LINE_ERROR:
+            fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
+                    ininame,
+                    lineno);
+            fprintf(stderr, "-> %s\n", line);
+            errs++ ;
+            break;
+
+            default:
+            break ;
+        }
+        memset(line, 0, ASCIILINESZ);
+        last=0;
+        if (errs<0) {
+            fprintf(stderr, "iniparser: memory allocation failure\n");
+            break ;
+        }
+    }
+    if (errs) {
+        dictionary_del(dict);
+        dict = NULL ;
+    }
+    fclose(in);
+    return dict ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Free all memory associated to an ini dictionary
+  @param    d Dictionary to free
+  @return   void
+
+  Free all memory associated to an ini dictionary.
+  It is mandatory to call this function before the dictionary object
+  gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d)
+{
+    dictionary_del(d);
+}
+
+/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/sc/iniparser/iniparser.h b/sc/iniparser/iniparser.h
new file mode 100644
index 0000000..72b0fc5
--- /dev/null
+++ b/sc/iniparser/iniparser.h
@@ -0,0 +1,328 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    iniparser.h
+   @author  N. Devillard
+   @brief   Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _INIPARSER_H_
+#define _INIPARSER_H_
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * The following #include is necessary on many Unixes but not Linux.
+ * It is not needed for Windows platforms.
+ * Uncomment it if needed.
+ */
+#include <sc_config.h>
+#ifdef SC_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "dictionary.h"
+
+/*---------------------------------------------------------------------------
+                                Protect C++ compilation
+ ---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get number of sections in a dictionary
+  @param    d   Dictionary to examine
+  @return   int Number of sections found in dictionary
+
+  This function returns the number of sections found in a dictionary.
+  The test to recognize sections is done on the string stored in the
+  dictionary: a section name is given as "section" whereas a key is
+  stored as "section:key", thus the test looks for entries that do not
+  contain a colon.
+
+  This clearly fails in the case a section name contains a colon, but
+  this should simply be avoided.
+
+  This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+int iniparser_getnsec(dictionary * d);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get name for section n in a dictionary.
+  @param    d   Dictionary to examine
+  @param    n   Section number (from 0 to nsec-1).
+  @return   Pointer to char string
+
+  This function locates the n-th section in a dictionary and returns
+  its name as a pointer to a string statically allocated inside the
+  dictionary. Do not free or modify the returned string!
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+char * iniparser_getsecname(dictionary * d, int n);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given dictionary into a loadable ini file.
+  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dump_ini(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary section to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    s   Section name of dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given section of a given dictionary into a loadable ini
+  file.  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump.
+  @param    f   Opened file pointer to dump to.
+  @return   void
+
+  This function prints out the contents of a dictionary, one element by
+  line, onto the provided file pointer. It is OK to specify @c stderr
+  or @c stdout as output files. This function is meant for debugging
+  purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   pointer to statically allocated character strings
+
+  This function queries a dictionary and finds all keys in a given section.
+  Each pointer in the returned char pointer-to-pointer is pointing to
+  a string allocated in the dictionary; do not free or modify them.
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key
+  @param    d       Dictionary to search
+  @param    key     Key string to look for
+  @param    def     Default value to return if key not found.
+  @return   pointer to statically allocated character string
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the pointer passed as 'def' is returned.
+  The returned char pointer is pointing to a string allocated in
+  the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to an int
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  Supported values for integers include the usual C notation
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+  are supported. Examples:
+
+  - "42"      ->  42
+  - "042"     ->  34 (octal -> decimal)
+  - "0x42"    ->  66 (hexa  -> decimal)
+
+  Warning: the conversion may overflow in various ways. Conversion is
+  totally outsourced to strtol(), see the associated man page for overflow
+  handling.
+
+  Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a double
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   double
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a boolean
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  A true boolean is found if one of the following is matched:
+
+  - A string starting with 'y'
+  - A string starting with 'Y'
+  - A string starting with 't'
+  - A string starting with 'T'
+  - A string starting with '1'
+
+  A false boolean is found if one of the following is matched:
+
+  - A string starting with 'n'
+  - A string starting with 'N'
+  - A string starting with 'f'
+  - A string starting with 'F'
+  - A string starting with '0'
+
+  The notfound value returned if no boolean is identified, does not
+  necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set an entry in a dictionary.
+  @param    ini     Dictionary to modify.
+  @param    entry   Entry to modify (entry name)
+  @param    val     New value to associate to the entry.
+  @return   int 0 if Ok, -1 otherwise.
+
+  If the given entry can be found in the dictionary, it is modified to
+  contain the provided value. If it cannot be found, -1 is returned.
+  It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete an entry in a dictionary
+  @param    ini     Dictionary to modify
+  @param    entry   Entry to delete (entry name)
+  @return   void
+
+  If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Finds out if a given entry exists in a dictionary
+  @param    ini     Dictionary to search
+  @param    entry   Name of the entry to look for
+  @return   integer 1 if entry exists, 0 otherwise
+
+  Finds out if a given entry exists in the dictionary. Since sections
+  are stored as keys with NULL associated values, this is the only way
+  of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(dictionary * ini, const char * entry) ;
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Parse an ini file and return an allocated dictionary object
+  @param    ininame Name of the ini file to read.
+  @return   Pointer to newly allocated dictionary
+
+  This is the parser for ini files. This function is called, providing
+  the name of the file to be read. It returns a dictionary object that
+  should not be accessed directly, but through accessor functions
+  instead.
+
+  The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Free all memory associated to an ini dictionary
+  @param    d Dictionary to free
+  @return   void
+
+  Free all memory associated to an ini dictionary.
+  It is mandatory to call this function before the dictionary object
+  gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d);
+
+#ifdef __cplusplus
+#if 0
+{
+#endif
+}
+#endif
+
+#endif
diff --git a/sc/iniparser/twisted.ini b/sc/iniparser/twisted.ini
new file mode 100644
index 0000000..65f581a
--- /dev/null
+++ b/sc/iniparser/twisted.ini
@@ -0,0 +1,131 @@
+#
+# Twisted.ini
+# This file is meant for regression tests
+
+# Different blank settings around the equal sign
+[blanks]
+a=1
+b=1;
+c=1; comment
+d=1# comment
+
+e =1
+f =1;
+g =1; comment
+h =1# comment
+
+i= 1
+j= 1;
+k= 1; comment
+l= 1# comment
+
+m = 1
+n = 1;
+o = 1; comment
+p = 1# comment
+
+q=1 ;
+r=1 ; comment
+s=1 ;comment
+t=1 #comment
+
+# Empty values
+[empty]
+a = ''
+b = ""
+
+c = '' ;
+d = "" ;
+
+e = '' ; comment
+f = "" ; comment
+
+g =
+h = ;
+i = ; comment
+j = # comment
+
+k=
+l=;
+m=;comment
+n=#
+
+# Peculiar values
+[peculiar]
+a=';';
+b='#'#
+c=';';comment
+d='#'#comment
+e=\;
+f=\#
+g=\;comment
+h=\#comment
+i=;;
+j=##
+k=;;;;;;;;;;
+l=##########
+
+# Quotes
+[quotes]
+s1='
+s2=''
+s3='''
+s4=''''
+
+d1="
+d2=""
+d3="""
+d4=""""
+
+m1='"'
+m2="'"
+
+h1=hello'world
+h2='hello'world
+h3='hello'world'
+
+h4=hello"world
+h5="hello"world
+h6="hello"world"
+
+# Section names
+[a]
+[ b]
+[c ]
+[ d ]
+[ begin    end ]
+[ open[ ]
+
+# Multi-line inputs
+[multi]
+a = begin\
+end
+b = begin \
+end
+c = begin \
+ end
+d = 1\
+2\
+3\
+4
+e = 1 \
+    2 \
+    3 \
+    4
+f = 1 ; \
+hidden = because of the preceding backslash multi-lining the comment ;
+visible = 1
+g = 1 #\
+and now this comment is hidden too \
+and this one too
+h = 1
+multi \
+line \
+key = 1
+multi \
+line \
+key = \
+multi \
+line \
+value ;
+# end of file
diff --git a/sc/libb64/AUTHORS b/sc/libb64/AUTHORS
new file mode 100644
index 0000000..af68737
--- /dev/null
+++ b/sc/libb64/AUTHORS
@@ -0,0 +1,7 @@
+libb64: Base64 Encoding/Decoding Routines
+======================================
+
+Authors:
+-------
+
+Chris Venter	chris.venter at gmail.com	http://rocketpod.blogspot.com
diff --git a/sc/libb64/CHANGELOG b/sc/libb64/CHANGELOG
new file mode 100644
index 0000000..27be0a5
--- /dev/null
+++ b/sc/libb64/CHANGELOG
@@ -0,0 +1,16 @@
+libb64: Base64 Encoding/Decoding Routines
+======================================
+
+## Changelog ##
+
+Version 1.1 Release
+-------------------
+Modified encode.h to (correctly) read from the iostream argument,
+instead of std::cin.
+Thanks to Peter K. Lee for the heads-up.
+
+No API changes.
+
+Version 1.0 Release
+-------------------
+The current content is the changeset.
diff --git a/sc/libb64/LICENSE b/sc/libb64/LICENSE
new file mode 100644
index 0000000..ae8a7b9
--- /dev/null
+++ b/sc/libb64/LICENSE
@@ -0,0 +1,29 @@
+Copyright-Only Dedication (based on United States law)
+or Public Domain Certification
+
+The person or persons who have associated work with this document (the
+"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
+his knowledge, the work of authorship identified is in the public domain of the
+country from which the work is published, or (b) hereby dedicates whatever
+copyright the dedicators holds in the work of authorship identified below (the
+"Work") to the public domain. A certifier, moreover, dedicates any copyright
+interest he may have in the associated work, and for these purposes, is
+described as a "dedicator" below.
+
+A certifier has taken reasonable steps to verify the copyright status of this
+work. Certifier recognizes that his good faith efforts may not shield him from
+liability if in fact the work certified is not in the public domain.
+
+Dedicator makes this dedication for the benefit of the public at large and to
+the detriment of the Dedicator's heirs and successors. Dedicator intends this
+dedication to be an overt act of relinquishment in perpetuity of all present
+and future rights under copyright law, whether vested or contingent, in the
+Work. Dedicator understands that such relinquishment of all rights includes
+the relinquishment of all rights to enforce (by lawsuit or otherwise) those
+copyrights in the Work.
+
+Dedicator recognizes that, once placed in the public domain, the Work may be
+freely reproduced, distributed, transmitted, used, modified, built upon, or
+otherwise exploited by anyone for any purpose, commercial or non-commercial,
+and in any way, including by methods that have not yet been invented or
+conceived.
diff --git a/sc/libb64/Makefile.am b/sc/libb64/Makefile.am
new file mode 100644
index 0000000..8128f4b
--- /dev/null
+++ b/sc/libb64/Makefile.am
@@ -0,0 +1,24 @@
+
+# This file is part of the SC Library
+# Makefile.am in libb64
+# included non-recursively from toplevel directory
+
+bin_PROGRAMS += libb64/sc_b64enc libb64/sc_b64dec
+libb64_sc_b64enc_SOURCES = libb64/b64enc.c
+libb64_sc_b64enc_CPPFLAGS = $(AM_CPPFLAGS) -I at top_srcdir@/libb64
+libb64_sc_b64dec_SOURCES = libb64/b64dec.c
+libb64_sc_b64dec_CPPFLAGS = $(AM_CPPFLAGS) -I at top_srcdir@/libb64
+
+libb64_internal_headers = libb64/libb64.h
+libb64_compiled_sources = libb64/cencode.c libb64/cdecode.c
+
+libsc_internal_headers += $(libb64_internal_headers)
+libsc_compiled_sources += $(libb64_compiled_sources)
+LIBSC_CPPFLAGS += -I at top_srcdir@/libb64
+
+EXTRA_DIST += \
+	libb64/AUTHORS libb64/CHANGELOG libb64/LICENSE libb64/README
+
+LINT_CSOURCES += \
+	$(libb64_sc_b64enc_SOURCES) \
+	$(libb64_sc_b64dec_SOURCES)
diff --git a/sc/libb64/README b/sc/libb64/README
new file mode 100644
index 0000000..be1a9ce
--- /dev/null
+++ b/sc/libb64/README
@@ -0,0 +1,142 @@
+b64: Base64 Encoding/Decoding Routines
+======================================
+
+Overview:
+--------
+libb64 is a library of ANSI C routines for fast encoding/decoding data into and
+from a base64-encoded format. C++ wrappers are included, as well as the source
+code for standalone encoding and decoding executables.
+
+base64 consists of ASCII text, and is therefore a useful encoding for storing
+binary data in a text file, such as xml, or sending binary data over text-only
+email.
+
+References:
+----------
+* Wikipedia article:
+	http://en.wikipedia.org/wiki/Base64
+* base64, another implementation of a commandline en/decoder:
+	http://www.fourmilab.ch/webtools/base64/
+
+Why?
+---
+I did this because I need an implementation of base64 encoding and decoding,
+without any licensing problems. Most OS implementations are released under
+either the GNU/GPL, or a BSD-variant, which is not what I require.
+
+Also, the chance to actually use the co-routine implementation in code is rare,
+and its use here is fitting. I couldn't pass up the chance.
+For more information on this technique, see "Coroutines in C", by Simon Tatham,
+which can be found online here:
+http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
+
+So then, under which license do I release this code? On to the next section...
+
+License:
+-------
+This work is released under into the Public Domain.
+It basically boils down to this: I put this work in the public domain, and you
+can take it and do whatever you want with it.
+
+An example of this "license" is the Creative Commons Public Domain License, a
+copy of which can be found in the LICENSE file, and also online at
+http://creativecommons.org/licenses/publicdomain/
+
+Commandline Use:
+---------------
+Two pairs of executables are available:
+*) b64enc and b64dec, and
+*) encoder and decoder
+
+Both pairs function in the same way: the encoding half accepts data from the
+standard input, and spits out the base64-encoded data to standard output.
+The decoding half does the reverse: it accepts base64-encoded data on standard
+input, and spits out the plain data on standard output.
+
+This allows for direct use, as well as easy integration into a piped command.
+
+For example, to encode file, run
+$ cat file | ./encode > file.txt
+and to decode the text into the original file
+$ cat file.txt | ./decode > file2
+
+file and file2 is 100% identical, file.txt is a 100% pure ASCII file.
+
+Programming:
+-----------
+Some C++ wrappers are provided as well, so you don't have to get your hands
+dirty. Encoding from standard input to standard output is as simple as
+
+	#include <b64/encode.h>
+	#include <iostream>
+	int main()
+	{
+		base64::encoder E;
+		E.encode(std::cin, std::cout);
+		return 0;
+	}
+
+Both standalone executables and a static library is provided in the package,
+
+Implementation:
+--------------
+It is DAMN fast, if I may say so myself. The C code uses a little trick which
+has been used to implement coroutines, of which one can say that this
+implementation is an example.
+
+The trick involves the fact that a switch-statement may legally cross into
+sub-blocks. A very thorough and enlightening essay on co-routines in C, using
+this method, can be found in the above mentioned "Coroutines in C", by Simon
+Tatham: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
+
+For example, an RLE decompressing routine, adapted from the article:
+1	static int STATE = 0;
+2	static int len, c;
+3	switch (STATE)
+4	{
+5		while (1)
+6		{
+7			c = getchar();
+8			if (c == EOF) return EOF;
+9			if (c == 0xFF) {
+10				len = getchar();
+11				c = getchar();
+12				while (len--)
+13				{
+14					STATE = 0;
+15					return c;
+16	case 0:
+17				}
+18			} else
+19				STATE = 1;
+20				return c;
+21	case 1:
+22			}
+23		}
+24	}
+
+As can be seen from this example, a coroutine depends on a state variable,
+which it sets directly before exiting (lines 14 and 119). The next time the
+routine is entered, the switch moves control to the specific point directly
+after the previous exit (lines 16 and 21).hands
+
+(As an aside, in the mentioned article the combination of the top-level switch,
+the various setting of the state, the return of a value, and the labelling of
+the exit point is wrapped in #define macros, making the structure of the
+routine even clearer.)
+
+The obvious problem with any such routine is the static keyword.
+Any static variables in a function spell doom for multithreaded applications.
+Also, in situations where this coroutine is used by more than one other
+coroutines, the consistency is disturbed.
+
+What is needed is a structure for storing these variabled, which is passed to
+the routine seperately. This obviously breaks the modularity of the function,
+since now the caller has to worry about and care for the internal state of the
+routine (the callee). This allows for a fast, multithreading-enabled
+implementation, which may (obviously) be wrapped in a C++ object for ease of
+use.
+
+The base64 encoding and decoding functionality in this package is implemented
+in exactly this way, providing both a high-speed high-maintanence C interface,
+and a wrapped C++ which is low-maintanence and only slightly less performant.
diff --git a/sc/libb64/b64dec.c b/sc/libb64/b64dec.c
new file mode 100644
index 0000000..ec2152c
--- /dev/null
+++ b/sc/libb64/b64dec.c
@@ -0,0 +1,43 @@
+
+/*
+ * adapted from libb64 by CB
+ */
+
+/*
+b64dec.c - c source to a base64 decoder
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include <libb64.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+  const size_t        readsize = 4096;
+  char               *code = 0;
+  char               *plaintext = 0;
+  size_t              codelength;
+  size_t              plainlength;
+  base64_decodestate  state;
+
+  code = (char *) malloc (sizeof (char) * readsize);
+  plaintext = (char *) malloc (sizeof (char) * readsize);
+
+  base64_init_decodestate (&state);
+
+  do {
+    codelength = fread ((void *) code, sizeof (char), readsize, stdin);
+    plainlength = base64_decode_block (code, codelength, plaintext, &state);
+    (void) fwrite ((void *) plaintext, sizeof (char), plainlength, stdout);
+  }
+  while (!feof (stdin) && codelength > 0);
+
+  free (code);
+  free (plaintext);
+
+  return 0;
+}
diff --git a/sc/libb64/b64enc.c b/sc/libb64/b64enc.c
new file mode 100644
index 0000000..0288017
--- /dev/null
+++ b/sc/libb64/b64enc.c
@@ -0,0 +1,46 @@
+
+/*
+ * adapted from libb64 by CB
+ */
+
+/*
+b64enc.c - c source to a base64 encoder
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include <libb64.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+  const size_t        readsize = 4096;
+  char               *plaintext = 0;
+  char               *code = 0;
+  size_t              plainlength;
+  size_t              codelength;
+  base64_encodestate  state;
+
+  code = (char *) malloc (sizeof (char) * readsize * 2);
+  plaintext = (char *) malloc (sizeof (char) * readsize);
+
+  base64_init_encodestate (&state);
+
+  do {
+    plainlength = fread ((void *) plaintext, sizeof (char), readsize, stdin);
+    codelength = base64_encode_block (plaintext, plainlength, code, &state);
+    (void) fwrite ((void *) code, sizeof (char), codelength, stdout);
+  }
+  while (!feof (stdin) && plainlength > 0);
+
+  codelength = base64_encode_blockend (code, &state);
+  (void) fwrite ((void *) code, sizeof (char), codelength, stdout);
+
+  free (code);
+  free (plaintext);
+
+  return 0;
+}
diff --git a/sc/libb64/cdecode.c b/sc/libb64/cdecode.c
new file mode 100644
index 0000000..1bc08ce
--- /dev/null
+++ b/sc/libb64/cdecode.c
@@ -0,0 +1,102 @@
+
+/*
+ * adapted from libb64 by CB
+ */
+
+/*
+cdecoder.c - c source to a base64 decoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include <libb64.h>
+
+static inline char
+base64_decode_value (char value_in)
+{
+  static const char   decoding[] =
+    { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1,
+    -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+    17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+    29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+    47, 48, 49, 50, 51
+  };
+  static const char   decoding_size = (char) sizeof (decoding);
+
+  value_in -= 43;
+  return (value_in < 0 || value_in >= decoding_size) ?
+    -1 : decoding[(int) value_in];
+}
+
+void
+base64_init_decodestate (base64_decodestate * state_in)
+{
+  state_in->step = step_a;
+  state_in->plainchar = 0;
+}
+
+size_t
+base64_decode_block (const char *code_in, size_t length_in,
+                     char *plaintext_out, base64_decodestate * state_in)
+{
+  /*@unused@ */
+  const char         *codechar = code_in;
+  char               *plainchar = plaintext_out;
+  /*@unused@ */
+  char                fragment;
+
+  *plainchar = state_in->plainchar;
+
+  switch (state_in->step) {
+    while (1) {
+  case step_a:
+      do {
+        if (codechar == code_in + length_in) {
+          state_in->step = step_a;
+          state_in->plainchar = *plainchar;
+          return (size_t) (plainchar - plaintext_out);
+        }
+        fragment = base64_decode_value (*codechar++);
+      } while (fragment < 0);
+      *plainchar = (char) ((fragment & 0x03f) << 2);
+  case step_b:
+      do {
+        if (codechar == code_in + length_in) {
+          state_in->step = step_b;
+          state_in->plainchar = *plainchar;
+          return (size_t) (plainchar - plaintext_out);
+        }
+        fragment = base64_decode_value (*codechar++);
+      } while (fragment < 0);
+      *plainchar = (char) (*plainchar | ((fragment & 0x030) >> 4));
+      ++plainchar;
+      *plainchar = (char) ((fragment & 0x00f) << 4);
+  case step_c:
+      do {
+        if (codechar == code_in + length_in) {
+          state_in->step = step_c;
+          state_in->plainchar = *plainchar;
+          return (size_t) (plainchar - plaintext_out);
+        }
+        fragment = base64_decode_value (*codechar++);
+      } while (fragment < 0);
+      *plainchar = (char) (*plainchar | ((fragment & 0x03c) >> 2));
+      ++plainchar;
+      *plainchar = (char) ((fragment & 0x003) << 6);
+  case step_d:
+      do {
+        if (codechar == code_in + length_in) {
+          state_in->step = step_d;
+          state_in->plainchar = *plainchar;
+          return (size_t) (plainchar - plaintext_out);
+        }
+        fragment = base64_decode_value (*codechar++);
+      } while (fragment < 0);
+      *plainchar = (char) (*plainchar | (fragment & 0x03f));
+      ++plainchar;
+    }
+  }
+  /* control should not reach here */
+  return (size_t) (plainchar - plaintext_out);
+}
diff --git a/sc/libb64/cencode.c b/sc/libb64/cencode.c
new file mode 100644
index 0000000..aaac330
--- /dev/null
+++ b/sc/libb64/cencode.c
@@ -0,0 +1,121 @@
+
+/*
+ * adapted from libb64 by CB
+ */
+
+/*
+cencoder.c - c source to a base64 encoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include <libb64.h>
+
+const int           CHARS_PER_LINE = 72;
+
+static inline char
+base64_encode_value (char value_in)
+{
+  static const char  *encoding =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+  return value_in > 63 ? '=' : encoding[(int) value_in];
+}
+
+void
+base64_init_encodestate (base64_encodestate * state_in)
+{
+  state_in->step = step_A;
+  state_in->result = 0;
+  state_in->stepcount = 0;
+}
+
+size_t
+base64_encode_block (const char *plaintext_in, size_t length_in,
+                     char *code_out, base64_encodestate * state_in)
+{
+  /*@unused@ */
+  const char         *plainchar = plaintext_in;
+  /*@unused@ */
+  const char         *const plaintextend = plaintext_in + length_in;
+  char               *codechar = code_out;
+  char                result;
+  /*@unused@ */
+  char                fragment;
+
+  result = state_in->result;
+
+  switch (state_in->step) {
+    while (1) {
+  case step_A:
+      if (plainchar == plaintextend) {
+        state_in->result = result;
+        state_in->step = step_A;
+        return (size_t) (codechar - code_out);
+      }
+      fragment = *plainchar++;
+      result = (char) ((fragment & 0x0fc) >> 2);
+      *codechar++ = base64_encode_value (result);
+      result = (char) ((fragment & 0x003) << 4);
+  case step_B:
+      if (plainchar == plaintextend) {
+        state_in->result = result;
+        state_in->step = step_B;
+        return (size_t) (codechar - code_out);
+      }
+      fragment = *plainchar++;
+      result = (char) (result | ((fragment & 0x0f0) >> 4));
+      *codechar++ = base64_encode_value (result);
+      result = (char) ((fragment & 0x00f) << 2);
+  case step_C:
+      if (plainchar == plaintextend) {
+        state_in->result = result;
+        state_in->step = step_C;
+        return (size_t) (codechar - code_out);
+      }
+      fragment = *plainchar++;
+      result = (char) (result | ((fragment & 0x0c0) >> 6));
+      *codechar++ = base64_encode_value (result);
+      result = (char) ((fragment & 0x03f) >> 0);
+      *codechar++ = base64_encode_value (result);
+
+      ++(state_in->stepcount);
+      /* CB: disable wrapping by default */
+#ifdef SC_BASE64_WRAP
+      if (state_in->stepcount == CHARS_PER_LINE / 4) {
+        *codechar++ = '\n';
+        state_in->stepcount = 0;
+      }
+#endif
+    }
+  }
+  /* control should not reach here */
+  return (size_t) (codechar - code_out);
+}
+
+size_t
+base64_encode_blockend (char *code_out, base64_encodestate * state_in)
+{
+  char               *codechar = code_out;
+
+  switch (state_in->step) {
+  case step_B:
+    *codechar++ = base64_encode_value (state_in->result);
+    *codechar++ = '=';
+    *codechar++ = '=';
+    break;
+  case step_C:
+    *codechar++ = base64_encode_value (state_in->result);
+    *codechar++ = '=';
+    break;
+  case step_A:
+    break;
+  }
+  /* CB: remove final newline by default */
+#ifdef SC_BASE64_WRAP
+  *codechar++ = '\n';
+#endif
+
+  return (size_t) (codechar - code_out);
+}
diff --git a/sc/libb64/libb64.h b/sc/libb64/libb64.h
new file mode 100644
index 0000000..d208a30
--- /dev/null
+++ b/sc/libb64/libb64.h
@@ -0,0 +1,139 @@
+
+/*
+ * adapted from libb64 by CB
+ */
+
+/* #define SC_BASE64_WRAP */
+
+#include <stdlib.h>
+
+/*
+cdecode.h - c header for a base64 decoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CDECODE_H
+#define BASE64_CDECODE_H
+
+#ifdef __cplusplus
+extern              "C"
+{
+#if 0
+}
+#endif
+#endif
+
+typedef enum
+{
+  step_a, step_b, step_c, step_d
+}
+base64_decodestep;
+
+typedef struct
+{
+  base64_decodestep   step;
+  char                plainchar;
+}
+base64_decodestate;
+
+/** This function needs to be called to initialize the internal decoder state.
+ * Does not allocate any memory so no cleanup function is necessary after use.
+ * \param [out] state_in        Internal state of decoder.
+ */
+void                base64_init_decodestate (base64_decodestate * state_in);
+
+/** Decode a chunk of data.
+ * This function can be called multiple times for the same state_in.
+ * \param [in] code_in          Data in base64 encoding.
+ * \param [in] length_in        Length of code_in in bytes.
+ * \param [out] plaintext_out   Memory of at least length_in bytes that will
+ *                              contain the plaintext on output.
+ * \param [in,out] state_in     Internal state of decoder.
+ * \return                      Byte length of decoded data in plaintext_out.
+ */
+size_t              base64_decode_block (const char *code_in,
+                                         size_t length_in,
+                                         char *plaintext_out,
+                                         base64_decodestate * state_in);
+
+#ifdef __cplusplus
+#if 0
+{
+#endif
+}
+#endif
+
+#endif /* BASE64_CDECODE_H */
+
+/*
+cencode.h - c header for a base64 encoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CENCODE_H
+#define BASE64_CENCODE_H
+
+#ifdef __cplusplus
+extern              "C"
+{
+#if 0
+}
+#endif
+#endif
+
+typedef enum
+{
+  step_A, step_B, step_C
+}
+base64_encodestep;
+
+typedef struct
+{
+  base64_encodestep   step;
+  char                result;
+  int                 stepcount;
+}
+base64_encodestate;
+
+/** This function needs to be called to initialize the internal encoder state.
+ * Does not allocate any memory so no cleanup function is necessary after use.
+ * \param [out] state_in        Internal state of encoder.
+ */
+void                base64_init_encodestate (base64_encodestate * state_in);
+
+/** Encode a chunk of data.
+ * This function can be called multiple times for the same state_in.
+ * \param [in] plaintext_in     Data to be base64 encoded.
+ * \param [in] length_in        Length of plaintext_in in bytes.
+ * \param [out] code_out        Memory of at least 2 * length_in that will
+ *                              contain the base64 encoded data on output.
+ * \param [in,out] state_in     Internal state of encoder.
+ * \return                      Byte length of encoded data in code_out.
+ */
+size_t              base64_encode_block (const char *plaintext_in,
+                                         size_t length_in, char *code_out,
+                                         base64_encodestate * state_in);
+
+/** Flush remaining code bytes after all input data have been encoded.
+ * Must be called when the encoding is done to create valid base64 data.
+ * \param [out] code_out        Memory of at least 4 bytes that will contain
+ *                              the final encoded bits.
+ * \param [in,out] state_in     Internal state of encoder.
+ *                              Needs base64_init_encodestate to be used again.
+ * \return                      Number of final bytes written to code_out.
+ */
+size_t              base64_encode_blockend (char *code_out,
+                                            base64_encodestate * state_in);
+
+#ifdef __cplusplus
+#if 0
+{
+#endif
+}
+#endif
+
+#endif /* BASE64_CENCODE_H */
diff --git a/sc/scindent b/sc/scindent
new file mode 100755
index 0000000..b9b2b09
--- /dev/null
+++ b/sc/scindent
@@ -0,0 +1,40 @@
+#! /bin/bash
+
+# This is the GNU style for indent 2.2.9 (according to man page)
+#
+#"$INDENT" \
+#    -nbad -bap -nbc -bbo -bl -bli2 -bls -ncdb -nce -cp1 -cs -di2 \
+#    -ndj -nfc1 -nfca -hnl -i2 -ip5 -lp -pcs -nprs -psl -saf -sai \
+#    -saw -nsc -nsob
+#    "$@"
+
+# This is our modification
+#
+# blank line after procedure body
+# braces indent 0 (or do you want -bl2 here and -bl below?)
+# braces to right of control statements (or do you want -bl here?)
+# no tabs
+# put the return type of a function on a separate line
+# swallow optional blank lines
+INDENT_OPTIONS="-bap -bli0 -br -nut -psl -sob -di20"
+
+INDENT=`which gnuindent 2> /dev/null`
+if test -z "$INDENT" ; then
+	INDENT=`which gindent`
+fi
+if test -z "$INDENT" ; then
+	INDENT=`which indent`
+fi
+
+for arg in "$@" ; do
+  if [ "x$arg" == "x-o" ]; then
+    WANTSOUT=1
+  fi
+done
+if [ -z "$WANTSOUT" ]; then
+  for NAME in "$@" ; do
+    $INDENT $INDENT_OPTIONS "$NAME"
+  done
+else
+  $INDENT $INDENT_OPTIONS $@
+fi
diff --git a/sc/src/Makefile.am b/sc/src/Makefile.am
new file mode 100644
index 0000000..19d6f2a
--- /dev/null
+++ b/sc/src/Makefile.am
@@ -0,0 +1,53 @@
+
+# This file is part of the SC Library
+# Makefile.am in src
+# included non-recursively from toplevel directory
+
+# append to these variables in subdirectories as necessary
+libsc_generated_headers = src/sc_config.h
+libsc_installed_headers = \
+        src/sc.h src/sc_mpi.h src/sc_containers.h src/sc_avl.h \
+        src/sc_options.h src/sc_functions.h src/sc_statistics.h \
+        src/sc_ranges.h src/sc_io.h \
+        src/sc_amr.h src/sc_search.h src/sc_sort.h \
+        src/sc_dmatrix.h src/sc_blas.h src/sc_lapack.h \
+        src/sc_bspline.h src/sc_flops.h \
+        src/sc_getopt.h src/sc_obstack.h \
+        src/sc_lua.h \
+	src/sc_keyvalue.h src/sc_warp.h \
+        src/sc_allgather.h src/sc_reduce.h src/sc_notify.h
+libsc_internal_headers =
+libsc_compiled_sources = \
+        src/sc.c src/sc_mpi.c src/sc_containers.c src/sc_avl.c \
+        src/sc_options.c src/sc_functions.c src/sc_statistics.c \
+        src/sc_ranges.c src/sc_io.c \
+        src/sc_amr.c src/sc_search.c src/sc_sort.c \
+        src/sc_dmatrix.c src/sc_blas.c src/sc_lapack.c \
+        src/sc_bspline.c src/sc_flops.c \
+        src/sc_getopt.c src/sc_obstack.c src/sc_getopt1.c \
+	src/sc_keyvalue.c src/sc_warp.c \
+        src/sc_allgather.c src/sc_reduce.c src/sc_notify.c
+libsc_original_headers = \
+        src/sc_builtin/getopt.h src/sc_builtin/getopt_int.h \
+        src/sc_builtin/obstack.h
+
+# this variable is used for headers that are not publicly installed
+LIBSC_CPPFLAGS =
+
+lib_LTLIBRARIES += src/libsc.la
+src_libsc_la_SOURCES = \
+        $(libsc_internal_headers) \
+        $(libsc_compiled_sources)
+src_libsc_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBSC_CPPFLAGS)
+src_libsc_la_LDFLAGS = -release $(VERSION)
+LDADD += @top_builddir@/src/libsc.la
+
+nodist_include_HEADERS += $(libsc_generated_headers)
+include_HEADERS += $(libsc_installed_headers)
+
+scbuiltindir = $(includedir)/sc_builtin
+scbuiltin_HEADERS = $(libsc_original_headers)
+
+AM_CPPFLAGS += -I at top_srcdir@/src
+
+LINT_CSOURCES += $(libsc_compiled_sources)
diff --git a/sc/src/sc.c b/sc/src/sc.c
new file mode 100644
index 0000000..ee4c8a2
--- /dev/null
+++ b/sc/src/sc.c
@@ -0,0 +1,1074 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc.h>
+
+#ifdef SC_HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+typedef void        (*sc_sig_t) (int);
+
+#ifdef SC_HAVE_BACKTRACE
+#ifdef SC_HAVE_BACKTRACE_SYMBOLS
+#ifdef SC_HAVE_EXECINFO_H
+#include <execinfo.h>
+#define SC_BACKTRACE
+#define SC_STACK_SIZE 64
+#endif
+#endif
+#endif
+
+#ifdef SC_ENABLE_PTHREAD
+#include <pthread.h>
+#endif
+
+typedef struct sc_package
+{
+  int                 is_registered;
+  sc_log_handler_t    log_handler;
+  int                 log_threshold;
+  int                 log_indent;
+  int                 malloc_count;
+  int                 free_count;
+  const char         *name;
+  const char         *full;
+#ifdef SC_ENABLE_PTHREAD
+  pthread_mutex_t     mutex;
+#endif
+}
+sc_package_t;
+
+/** The only log handler that comes with libsc. */
+static void         sc_log_handler (FILE * log_stream,
+                                    const char *filename, int lineno,
+                                    int package, int category, int priority,
+                                    const char *msg);
+
+/* *INDENT-OFF* */
+const int sc_log2_lookup_table[256] =
+{ -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+   4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+   5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+   5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+};
+/* *INDENT-ON* */
+
+int                 sc_package_id = -1;
+FILE               *sc_trace_file = NULL;
+int                 sc_trace_prio = SC_LP_STATISTICS;
+
+static int          default_malloc_count = 0;
+static int          default_free_count = 0;
+
+static int          sc_identifier = -1;
+static sc_MPI_Comm  sc_mpicomm = sc_MPI_COMM_NULL;
+
+static FILE        *sc_log_stream = NULL;
+static sc_log_handler_t sc_default_log_handler = sc_log_handler;
+static int          sc_default_log_threshold = SC_LP_THRESHOLD;
+
+static int          sc_signals_caught = 0;
+static sc_sig_t     system_int_handler = NULL;
+static sc_sig_t     system_segv_handler = NULL;
+static sc_sig_t     system_usr2_handler = NULL;
+
+static int          sc_print_backtrace = 0;
+
+static int          sc_num_packages = 0;
+static int          sc_num_packages_alloc = 0;
+static sc_package_t *sc_packages = NULL;
+
+#ifdef SC_ENABLE_PTHREAD
+
+static pthread_mutex_t sc_default_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t sc_error_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void
+sc_check_abort_thread (int condition, int package, const char *message)
+{
+  if (!condition) {
+    pthread_mutex_lock (&sc_error_mutex);
+    printf ("[libsc] sc_check_abort_thread %d %s\n", package, message);
+    abort ();
+    /* abort () will not return */
+    pthread_mutex_unlock (&sc_error_mutex);
+  }
+}
+
+static inline pthread_mutex_t *
+sc_package_mutex (int package)
+{
+  if (package == -1) {
+    return &sc_default_mutex;
+  }
+  else {
+#ifdef SC_ENABLE_DEBUG
+    sc_check_abort_thread (sc_package_is_registered (package),
+                           package, "sc_package_mutex");
+#endif
+    return &sc_packages[package].mutex;
+  }
+}
+
+static inline void
+sc_package_lock (int package)
+{
+  pthread_mutex_t    *mutex = sc_package_mutex (package);
+  int                 pth;
+
+  pth = pthread_mutex_lock (mutex);
+  sc_check_abort_thread (pth == 0, package, "sc_package_lock");
+}
+
+static inline void
+sc_package_unlock (int package)
+{
+  pthread_mutex_t    *mutex = sc_package_mutex (package);
+  int                 pth;
+
+  pth = pthread_mutex_unlock (mutex);
+  sc_check_abort_thread (pth == 0, package, "sc_package_unlock");
+}
+
+#endif /* SC_ENABLE_PTHREAD */
+
+static void
+sc_signal_handler (int sig)
+{
+  const char         *sigstr;
+
+  switch (sig) {
+  case SIGINT:
+    sigstr = "INT";
+    break;
+  case SIGSEGV:
+    sigstr = "SEGV";
+    break;
+  case SIGUSR2:
+    sigstr = "USR2";
+    break;
+  default:
+    sigstr = "<unknown>";
+    break;
+  }
+  SC_LERRORF ("Caught signal %s\n", sigstr);
+
+  sc_abort ();
+}
+
+/** Installs or removes a signal handler for INT SEGV USR2 that aborts.
+ * \param [in] catch    If true, catch signals INT SEGV USR2.
+ *                      If false, reinstate previous signal handler.
+ */
+static void
+sc_set_signal_handler (int catch_signals)
+{
+  if (catch_signals && !sc_signals_caught) {
+    system_int_handler = signal (SIGINT, sc_signal_handler);
+    SC_CHECK_ABORT (system_int_handler != SIG_ERR, "catching INT");
+    system_segv_handler = signal (SIGSEGV, sc_signal_handler);
+    SC_CHECK_ABORT (system_segv_handler != SIG_ERR, "catching SEGV");
+    system_usr2_handler = signal (SIGUSR2, sc_signal_handler);
+    SC_CHECK_ABORT (system_usr2_handler != SIG_ERR, "catching USR2");
+    sc_signals_caught = 1;
+  }
+  else if (!catch_signals && sc_signals_caught) {
+    (void) signal (SIGINT, system_int_handler);
+    system_int_handler = NULL;
+    (void) signal (SIGSEGV, system_segv_handler);
+    system_segv_handler = NULL;
+    (void) signal (SIGUSR2, system_usr2_handler);
+    system_usr2_handler = NULL;
+    sc_signals_caught = 0;
+  }
+}
+
+static void
+sc_log_handler (FILE * log_stream, const char *filename, int lineno,
+                int package, int category, int priority, const char *msg)
+{
+  int                 wp = 0, wi = 0;
+  int                 lindent = 0;
+
+  if (package != -1) {
+    if (!sc_package_is_registered (package))
+      package = -1;
+    else {
+      wp = 1;
+      lindent = sc_packages[package].log_indent;
+    }
+  }
+  wi = (category == SC_LC_NORMAL && sc_identifier >= 0);
+
+  if (wp || wi) {
+    fputc ('[', log_stream);
+    if (wp)
+      fprintf (log_stream, "%s", sc_packages[package].name);
+    if (wp && wi)
+      fputc (' ', log_stream);
+    if (wi)
+      fprintf (log_stream, "%d", sc_identifier);
+    fprintf (log_stream, "] %*s", lindent, "");
+  }
+
+  if (priority == SC_LP_TRACE) {
+    char                bn[BUFSIZ], *bp;
+
+    snprintf (bn, BUFSIZ, "%s", filename);
+    bp = basename (bn);
+    fprintf (log_stream, "%s:%d ", bp, lineno);
+  }
+
+  fputs (msg, log_stream);
+  fflush (log_stream);
+}
+
+static int         *
+sc_malloc_count (int package)
+{
+  if (package == -1)
+    return &default_malloc_count;
+
+  SC_ASSERT (sc_package_is_registered (package));
+  return &sc_packages[package].malloc_count;
+}
+
+static int         *
+sc_free_count (int package)
+{
+  if (package == -1)
+    return &default_free_count;
+
+  SC_ASSERT (sc_package_is_registered (package));
+  return &sc_packages[package].free_count;
+}
+
+void               *
+sc_malloc (int package, size_t size)
+{
+  void               *ret;
+  int                *malloc_count = sc_malloc_count (package);
+
+  ret = malloc (size);
+  if (size > 0) {
+    SC_CHECK_ABORT (ret != NULL, "Allocation");
+  }
+
+#ifdef SC_ENABLE_PTHREAD
+  sc_package_lock (package);
+#endif
+  if (size > 0) {
+    ++*malloc_count;
+  }
+  else {
+    *malloc_count += ((ret == NULL) ? 0 : 1);
+  }
+#ifdef SC_ENABLE_PTHREAD
+  sc_package_unlock (package);
+#endif
+
+  return ret;
+}
+
+void               *
+sc_calloc (int package, size_t nmemb, size_t size)
+{
+  void               *ret;
+  int                *malloc_count = sc_malloc_count (package);
+
+  ret = calloc (nmemb, size);
+  if (nmemb * size > 0) {
+    SC_CHECK_ABORT (ret != NULL, "Allocation");
+  }
+
+#ifdef SC_ENABLE_PTHREAD
+  sc_package_lock (package);
+#endif
+  if (nmemb * size > 0) {
+    ++*malloc_count;
+  }
+  else {
+    *malloc_count += ((ret == NULL) ? 0 : 1);
+  }
+#ifdef SC_ENABLE_PTHREAD
+  sc_package_unlock (package);
+#endif
+
+  return ret;
+}
+
+void               *
+sc_realloc (int package, void *ptr, size_t size)
+{
+  if (ptr == NULL) {
+    return sc_malloc (package, size);
+  }
+  else if (size == 0) {
+    sc_free (package, ptr);
+    return NULL;
+  }
+  else {
+    void               *ret;
+
+    ret = realloc (ptr, size);
+    SC_CHECK_ABORT (ret != NULL, "Reallocation");
+
+    return ret;
+  }
+}
+
+char               *
+sc_strdup (int package, const char *s)
+{
+  size_t              len;
+  char               *d;
+
+  if (s == NULL) {
+    return NULL;
+  }
+
+  len = strlen (s) + 1;
+  d = (char *) sc_malloc (package, len);
+  memcpy (d, s, len);
+
+  return d;
+}
+
+void
+sc_free (int package, void *ptr)
+{
+  if (ptr != NULL) {
+    int                *free_count = sc_free_count (package);
+
+#ifdef SC_ENABLE_PTHREAD
+    sc_package_lock (package);
+#endif
+    ++*free_count;
+#ifdef SC_ENABLE_PTHREAD
+    sc_package_unlock (package);
+#endif
+  }
+  free (ptr);
+}
+
+int
+sc_memory_status (int package)
+{
+  sc_package_t       *p;
+
+  if (package == -1) {
+    return (default_malloc_count - default_free_count);
+  }
+  else {
+    SC_ASSERT (sc_package_is_registered (package));
+    p = sc_packages + package;
+    return (p->malloc_count - p->free_count);
+  }
+}
+
+void
+sc_memory_check (int package)
+{
+  sc_package_t       *p;
+
+  if (package == -1)
+    SC_CHECK_ABORT (default_malloc_count == default_free_count,
+                    "Memory balance (default)");
+  else {
+    SC_ASSERT (sc_package_is_registered (package));
+    p = sc_packages + package;
+    SC_CHECK_ABORTF (p->malloc_count == p->free_count,
+                     "Memory balance (%s)", p->name);
+  }
+}
+
+int
+sc_int_compare (const void *v1, const void *v2)
+{
+  const int           i1 = *(int *) v1;
+  const int           i2 = *(int *) v2;
+
+  return i1 == i2 ? 0 : i1 < i2 ? -1 : +1;
+}
+
+int
+sc_int8_compare (const void *v1, const void *v2)
+{
+  const int8_t        i1 = *(int8_t *) v1;
+  const int8_t        i2 = *(int8_t *) v2;
+
+  return i1 == i2 ? 0 : i1 < i2 ? -1 : +1;
+}
+
+int
+sc_int16_compare (const void *v1, const void *v2)
+{
+  const int16_t       i1 = *(int16_t *) v1;
+  const int16_t       i2 = *(int16_t *) v2;
+
+  return i1 == i2 ? 0 : i1 < i2 ? -1 : +1;
+}
+
+int
+sc_int32_compare (const void *v1, const void *v2)
+{
+  const int32_t       i1 = *(int32_t *) v1;
+  const int32_t       i2 = *(int32_t *) v2;
+
+  return i1 == i2 ? 0 : i1 < i2 ? -1 : +1;
+}
+
+int
+sc_int64_compare (const void *v1, const void *v2)
+{
+  const int64_t       i1 = *(int64_t *) v1;
+  const int64_t       i2 = *(int64_t *) v2;
+
+  return i1 == i2 ? 0 : i1 < i2 ? -1 : +1;
+}
+
+int
+sc_double_compare (const void *v1, const void *v2)
+{
+  const double        d1 = *(double *) v1;
+  const double        d2 = *(double *) v2;
+
+  return d1 < d2 ? -1 : d1 > d2 ? 1 : 0;
+}
+
+void
+sc_set_log_defaults (FILE * log_stream,
+                     sc_log_handler_t log_handler, int log_threshold)
+{
+  sc_default_log_handler = log_handler != NULL ? log_handler : sc_log_handler;
+
+  if (log_threshold == SC_LP_DEFAULT) {
+    sc_default_log_threshold = SC_LP_THRESHOLD;
+  }
+  else {
+    SC_ASSERT (log_threshold >= SC_LP_ALWAYS
+               && log_threshold <= SC_LP_SILENT);
+    sc_default_log_threshold = log_threshold;
+  }
+
+  sc_log_stream = log_stream;
+}
+
+void
+sc_log (const char *filename, int lineno,
+        int package, int category, int priority, const char *msg)
+{
+  int                 log_threshold;
+  sc_log_handler_t    log_handler;
+  sc_package_t       *p;
+
+  if (package != -1 && !sc_package_is_registered (package)) {
+    package = -1;
+  }
+  if (package == -1) {
+    p = NULL;
+    log_threshold = sc_default_log_threshold;
+    log_handler = sc_default_log_handler;
+  }
+  else {
+    p = sc_packages + package;
+    log_threshold =
+      (p->log_threshold ==
+       SC_LP_DEFAULT) ? sc_default_log_threshold : p->log_threshold;
+    log_handler =
+      (p->log_handler == NULL) ? sc_default_log_handler : p->log_handler;
+  }
+  if (!(category == SC_LC_NORMAL || category == SC_LC_GLOBAL))
+    return;
+  if (!(priority > SC_LP_ALWAYS && priority < SC_LP_SILENT))
+    return;
+  if (category == SC_LC_GLOBAL && sc_identifier > 0)
+    return;
+
+#ifdef SC_ENABLE_PTHREAD
+  sc_package_lock (package);
+#endif
+  if (sc_trace_file != NULL && priority >= sc_trace_prio)
+    log_handler (sc_trace_file, filename, lineno,
+                 package, category, priority, msg);
+
+  if (priority >= log_threshold)
+    log_handler (sc_log_stream != NULL ? sc_log_stream : stdout,
+                 filename, lineno, package, category, priority, msg);
+#ifdef SC_ENABLE_PTHREAD
+  sc_package_unlock (package);
+#endif
+}
+
+void
+sc_logf (const char *filename, int lineno,
+         int package, int category, int priority, const char *fmt, ...)
+{
+  va_list             ap;
+
+  va_start (ap, fmt);
+  sc_logv (filename, lineno, package, category, priority, fmt, ap);
+  va_end (ap);
+}
+
+void
+sc_logv (const char *filename, int lineno,
+         int package, int category, int priority, const char *fmt, va_list ap)
+{
+  char                buffer[BUFSIZ];
+
+#ifdef SC_ENABLE_PTHREAD
+  sc_package_lock (package);
+#endif
+  vsnprintf (buffer, BUFSIZ, fmt, ap);
+#ifdef SC_ENABLE_PTHREAD
+  sc_package_unlock (package);
+#endif
+  sc_log (filename, lineno, package, category, priority, buffer);
+}
+
+void
+sc_log_indent_push (void)
+{
+  sc_log_indent_push_count (sc_package_id, 1);
+}
+
+void
+sc_log_indent_pop (void)
+{
+  sc_log_indent_pop_count (sc_package_id, 1);
+}
+
+void
+sc_log_indent_push_count (int package, int count)
+{
+  /* TODO: figure out a version that makes sense with threads */
+#ifndef SC_ENABLE_PTHREAD
+  SC_ASSERT (package < sc_num_packages);
+
+  if (package >= 0) {
+    sc_packages[package].log_indent += SC_MAX (0, count);
+  }
+#endif
+}
+
+void
+sc_log_indent_pop_count (int package, int count)
+{
+  /* TODO: figure out a version that makes sense with threads */
+#ifndef SC_ENABLE_PTHREAD
+  int                 new_indent;
+
+  SC_ASSERT (package < sc_num_packages);
+
+  if (package >= 0) {
+    new_indent = sc_packages[package].log_indent - SC_MAX (0, count);
+    sc_packages[package].log_indent = SC_MAX (0, new_indent);
+  }
+#endif
+}
+
+void
+sc_abort (void)
+{
+  if (0) {
+  }
+#ifdef SC_BACKTRACE
+  else if (sc_print_backtrace) {
+    int                 i, bt_size;
+    void               *bt_buffer[SC_STACK_SIZE];
+    char              **bt_strings;
+    const char         *str;
+
+    bt_size = backtrace (bt_buffer, SC_STACK_SIZE);
+    bt_strings = backtrace_symbols (bt_buffer, bt_size);
+
+    SC_LERRORF ("Abort: Obtained %d stack frames\n", bt_size);
+
+#ifdef SC_ADDRTOLINE
+    /* implement pipe connection to addr2line */
+#endif
+
+    for (i = 0; i < bt_size; i++) {
+      str = strrchr (bt_strings[i], '/');
+      if (str != NULL) {
+        ++str;
+      }
+      else {
+        str = bt_strings[i];
+      }
+      SC_LERRORF ("Stack %d: %s\n", i, str);
+    }
+    free (bt_strings);
+  }
+#endif
+  else {
+    SC_LERROR ("Abort\n");
+  }
+
+  fflush (stdout);
+  fflush (stderr);
+  sleep (1);                    /* allow time for pending output */
+
+  if (sc_mpicomm != sc_MPI_COMM_NULL) {
+    sc_MPI_Abort (sc_mpicomm, 1);       /* terminate all MPI processes */
+  }
+  abort ();
+}
+
+void
+sc_abort_verbose (const char *filename, int lineno, const char *msg)
+{
+  SC_LERRORF ("Abort: %s\n", msg);
+  SC_LERRORF ("Abort: %s:%d\n", filename, lineno);
+  sc_abort ();
+}
+
+void
+sc_abort_verbosef (const char *filename, int lineno, const char *fmt, ...)
+{
+  va_list             ap;
+
+  va_start (ap, fmt);
+  sc_abort_verbosev (filename, lineno, fmt, ap);
+  va_end (ap);
+}
+
+void
+sc_abort_verbosev (const char *filename, int lineno,
+                   const char *fmt, va_list ap)
+{
+  char                buffer[BUFSIZ];
+
+  vsnprintf (buffer, BUFSIZ, fmt, ap);
+  sc_abort_verbose (filename, lineno, buffer);
+}
+
+void
+sc_abort_collective (const char *msg)
+{
+  int                 mpiret;
+
+  if (sc_mpicomm != sc_MPI_COMM_NULL) {
+    mpiret = sc_MPI_Barrier (sc_mpicomm);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  if (sc_is_root ()) {
+    SC_ABORT (msg);
+  }
+  else {
+    sleep (3);                  /* wait for root rank's sc_MPI_Abort ()... */
+    abort ();                   /* ... otherwise this may call sc_MPI_Abort () */
+  }
+}
+
+int
+sc_package_register (sc_log_handler_t log_handler, int log_threshold,
+                     const char *name, const char *full)
+{
+  int                 i;
+  sc_package_t       *p;
+  sc_package_t       *new_package = NULL;
+  int                 new_package_id = -1;
+
+  SC_CHECK_ABORT (log_threshold == SC_LP_DEFAULT ||
+                  (log_threshold >= SC_LP_ALWAYS
+                   && log_threshold <= SC_LP_SILENT),
+                  "Invalid package log threshold");
+  SC_CHECK_ABORT (strcmp (name, "default"), "Package default forbidden");
+  SC_CHECK_ABORT (strchr (name, ' ') == NULL,
+                  "Packages name contains spaces");
+
+  /* sc_packages is static and thus initialized to all zeros */
+  for (i = 0; i < sc_num_packages_alloc; ++i) {
+    p = sc_packages + i;
+    SC_CHECK_ABORTF (!p->is_registered || strcmp (p->name, name),
+                     "Package %s is already registered", name);
+  }
+
+  /* Try to find unused space in sc_packages  */
+  for (i = 0; i < sc_num_packages_alloc; ++i) {
+    p = sc_packages + i;
+    if (!p->is_registered) {
+      new_package = p;
+      new_package_id = i;
+      break;
+    }
+  }
+
+  /* realloc if the space in sc_packages is used up */
+  if (i == sc_num_packages_alloc) {
+    sc_packages = (sc_package_t *) realloc (sc_packages,
+                                            (2 * sc_num_packages_alloc +
+                                             1) * sizeof (sc_package_t));
+    SC_CHECK_ABORT (sc_packages, "Failed to allocate memory");
+    new_package = sc_packages + i;
+    new_package_id = i;
+    sc_num_packages_alloc = 2 * sc_num_packages_alloc + 1;
+
+    /* initialize new packages */
+    for (; i < sc_num_packages_alloc; i++) {
+      p = sc_packages + i;
+      p->is_registered = 0;
+      p->log_handler = NULL;
+      p->log_threshold = SC_LP_SILENT;
+      p->log_indent = 0;
+      p->malloc_count = 0;
+      p->free_count = 0;
+      p->name = NULL;
+      p->full = NULL;
+    }
+  }
+
+  new_package->is_registered = 1;
+  new_package->log_handler = log_handler;
+  new_package->log_threshold = log_threshold;
+  new_package->log_indent = 0;
+  new_package->malloc_count = 0;
+  new_package->free_count = 0;
+  new_package->name = name;
+  new_package->full = full;
+#ifdef SC_ENABLE_PTHREAD
+  i = pthread_mutex_init (&new_package->mutex, NULL);
+  SC_CHECK_ABORTF (i == 0, "Mutex init failed for package %s", name);
+#endif
+
+  ++sc_num_packages;
+  SC_ASSERT (sc_num_packages <= sc_num_packages_alloc);
+  SC_ASSERT (0 <= new_package_id && new_package_id < sc_num_packages);
+
+  return new_package_id;
+}
+
+int
+sc_package_is_registered (int package_id)
+{
+  SC_CHECK_ABORT (0 <= package_id, "Invalid package id");
+
+  return (package_id < sc_num_packages_alloc &&
+          sc_packages[package_id].is_registered);
+}
+
+void
+sc_package_unregister (int package_id)
+{
+#ifdef SC_ENABLE_PTHREAD
+  int                 i;
+#endif
+  sc_package_t       *p;
+
+  SC_CHECK_ABORT (sc_package_is_registered (package_id),
+                  "Package not registered");
+  sc_memory_check (package_id);
+
+  p = sc_packages + package_id;
+  p->is_registered = 0;
+  p->log_handler = NULL;
+  p->log_threshold = SC_LP_DEFAULT;
+  p->malloc_count = p->free_count = 0;
+#ifdef SC_ENABLE_PTHREAD
+  i = pthread_mutex_destroy (&p->mutex);
+  SC_CHECK_ABORTF (i == 0, "Mutex destroy failed for package %s", p->name);
+#endif
+  p->name = p->full = NULL;
+
+  --sc_num_packages;
+}
+
+void
+sc_package_print_summary (int log_priority)
+{
+  int                 i;
+  sc_package_t       *p;
+
+  SC_GEN_LOGF (sc_package_id, SC_LC_GLOBAL, log_priority,
+               "Package summary (%d total):\n", sc_num_packages);
+
+  /* sc_packages is static and thus initialized to all zeros */
+  for (i = 0; i < sc_num_packages_alloc; ++i) {
+    p = sc_packages + i;
+    if (p->is_registered) {
+      SC_GEN_LOGF (sc_package_id, SC_LC_GLOBAL, log_priority,
+                   "   %3d: %-15s +%d-%d   %s\n",
+                   i, p->name, p->malloc_count, p->free_count, p->full);
+    }
+  }
+}
+
+void
+sc_init (sc_MPI_Comm mpicomm,
+         int catch_signals, int print_backtrace,
+         sc_log_handler_t log_handler, int log_threshold)
+{
+  int                 w;
+  const char         *trace_file_name;
+  const char         *trace_file_prio;
+
+  sc_identifier = -1;
+  sc_mpicomm = sc_MPI_COMM_NULL;
+  sc_print_backtrace = print_backtrace;
+
+  if (mpicomm != sc_MPI_COMM_NULL) {
+    int                 mpiret;
+
+    sc_mpicomm = mpicomm;
+    mpiret = sc_MPI_Comm_rank (sc_mpicomm, &sc_identifier);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  sc_set_signal_handler (catch_signals);
+  sc_package_id = sc_package_register (log_handler, log_threshold,
+                                       "libsc", "The SC Library");
+
+  trace_file_name = getenv ("SC_TRACE_FILE");
+  if (trace_file_name != NULL) {
+    char                buffer[BUFSIZ];
+
+    if (sc_identifier >= 0) {
+      snprintf (buffer, BUFSIZ, "%s.%d.log", trace_file_name, sc_identifier);
+    }
+    else {
+      snprintf (buffer, BUFSIZ, "%s.log", trace_file_name);
+    }
+    SC_CHECK_ABORT (sc_trace_file == NULL, "Trace file not NULL");
+    sc_trace_file = fopen (buffer, "wb");
+    SC_CHECK_ABORT (sc_trace_file != NULL, "Trace file open");
+
+    trace_file_prio = getenv ("SC_TRACE_LP");
+    if (trace_file_prio != NULL) {
+      if (!strcmp (trace_file_prio, "SC_LP_TRACE")) {
+        sc_trace_prio = SC_LP_TRACE;
+      }
+      else if (!strcmp (trace_file_prio, "SC_LP_DEBUG")) {
+        sc_trace_prio = SC_LP_DEBUG;
+      }
+      else if (!strcmp (trace_file_prio, "SC_LP_VERBOSE")) {
+        sc_trace_prio = SC_LP_VERBOSE;
+      }
+      else if (!strcmp (trace_file_prio, "SC_LP_INFO")) {
+        sc_trace_prio = SC_LP_INFO;
+      }
+      else if (!strcmp (trace_file_prio, "SC_LP_STATISTICS")) {
+        sc_trace_prio = SC_LP_STATISTICS;
+      }
+      else if (!strcmp (trace_file_prio, "SC_LP_PRODUCTION")) {
+        sc_trace_prio = SC_LP_PRODUCTION;
+      }
+      else if (!strcmp (trace_file_prio, "SC_LP_ESSENTIAL")) {
+        sc_trace_prio = SC_LP_ESSENTIAL;
+      }
+      else if (!strcmp (trace_file_prio, "SC_LP_ERROR")) {
+        sc_trace_prio = SC_LP_ERROR;
+      }
+      else {
+        SC_ABORT ("Invalid trace priority");
+      }
+    }
+  }
+
+  w = 24;
+  SC_GLOBAL_ESSENTIALF ("This is %s\n", SC_PACKAGE_STRING);
+#if 0
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "F77", SC_F77);
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "FFLAGS", SC_FFLAGS);
+#endif
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "CPP", SC_CPP);
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "CPPFLAGS", SC_CPPFLAGS);
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "CC", SC_CC);
+#if 0
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "C_VERSION", SC_C_VERSION);
+#endif
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "CFLAGS", SC_CFLAGS);
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "LDFLAGS", SC_LDFLAGS);
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "LIBS", SC_LIBS);
+#if 0
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "BLAS_LIBS", SC_BLAS_LIBS);
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "LAPACK_LIBS", SC_LAPACK_LIBS);
+  SC_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "FLIBS", SC_FLIBS);
+#endif
+}
+
+void
+sc_finalize (void)
+{
+  int                 i;
+  int                 retval;
+
+  /* sc_packages is static and thus initialized to all zeros */
+  for (i = sc_num_packages_alloc - 1; i >= 0; --i)
+    if (sc_packages[i].is_registered)
+      sc_package_unregister (i);
+
+  SC_ASSERT (sc_num_packages == 0);
+  sc_memory_check (-1);
+
+  free (sc_packages);
+  sc_packages = NULL;
+  sc_num_packages_alloc = 0;
+
+  sc_set_signal_handler (0);
+  sc_mpicomm = sc_MPI_COMM_NULL;
+
+  sc_print_backtrace = 0;
+  sc_identifier = -1;
+
+  /* close trace file */
+  if (sc_trace_file != NULL) {
+    retval = fclose (sc_trace_file);
+    SC_CHECK_ABORT (!retval, "Trace file close");
+
+    sc_trace_file = NULL;
+  }
+}
+
+int
+sc_is_root (void)
+{
+  return sc_identifier <= 0;
+}
+
+/* enable logging for files compiled with C++ */
+
+#ifndef __cplusplus
+#undef SC_ABORTF
+#undef SC_CHECK_ABORTF
+#undef SC_GEN_LOGF
+#undef SC_GLOBAL_LOGF
+#undef SC_LOGF
+#undef SC_GLOBAL_TRACEF
+#undef SC_GLOBAL_LDEBUGF
+#undef SC_GLOBAL_VERBOSEF
+#undef SC_GLOBAL_INFOF
+#undef SC_GLOBAL_STATISTICSF
+#undef SC_GLOBAL_PRODUCTIONF
+#undef SC_GLOBAL_ESSENTIALF
+#undef SC_GLOBAL_LERRORF
+#undef SC_TRACEF
+#undef SC_LDEBUGF
+#undef SC_VERBOSEF
+#undef SC_INFOF
+#undef SC_STATISTICSF
+#undef SC_PRODUCTIONF
+#undef SC_ESSENTIALF
+#undef SC_LERRORF
+#endif
+
+#ifndef SC_SPLINT
+
+void
+SC_ABORTF (const char *fmt, ...)
+{
+  va_list             ap;
+
+  va_start (ap, fmt);
+  sc_abort_verbosev ("<unknown>", 0, fmt, ap);
+  va_end (ap);
+}
+
+void
+SC_CHECK_ABORTF (int success, const char *fmt, ...)
+{
+  va_list             ap;
+
+  if (!success) {
+    va_start (ap, fmt);
+    sc_abort_verbosev ("<unknown>", 0, fmt, ap);
+    va_end (ap);
+  }
+}
+
+void
+SC_GEN_LOGF (int package, int category, int priority, const char *fmt, ...)
+{
+  va_list             ap;
+
+  va_start (ap, fmt);
+  sc_logv ("<unknown>", 0, package, category, priority, fmt, ap);
+  va_end (ap);
+}
+
+void
+SC_GLOBAL_LOGF (int priority, const char *fmt, ...)
+{
+  va_list             ap;
+
+  va_start (ap, fmt);
+  sc_logv ("<unknown>", 0, sc_package_id, SC_LC_GLOBAL, priority, fmt, ap);
+  va_end (ap);
+}
+
+void
+SC_LOGF (int priority, const char *fmt, ...)
+{
+  va_list             ap;
+
+  va_start (ap, fmt);
+  sc_logv ("<unknown>", 0, sc_package_id, SC_LC_NORMAL, priority, fmt, ap);
+  va_end (ap);
+}
+
+#define SC_LOG_IMP(n,p)                                 \
+  void                                                  \
+  SC_GLOBAL_ ## n ## F (const char *fmt, ...)           \
+  {                                                     \
+    va_list             ap;                             \
+    va_start (ap, fmt);                                 \
+    sc_logv ("<unknown>", 0, sc_package_id,             \
+             SC_LC_GLOBAL, SC_LP_ ## p, fmt, ap);       \
+    va_end (ap);                                        \
+  }                                                     \
+  void                                                  \
+  SC_ ## n ## F (const char *fmt, ...)                  \
+  {                                                     \
+    va_list             ap;                             \
+    va_start (ap, fmt);                                 \
+    sc_logv ("<unknown>", 0, sc_package_id,             \
+             SC_LC_NORMAL, SC_LP_ ## p, fmt, ap);       \
+    va_end (ap);                                        \
+  }
+
+/* *INDENT-OFF* */
+SC_LOG_IMP (TRACE, TRACE)
+SC_LOG_IMP (LDEBUG, DEBUG)
+SC_LOG_IMP (VERBOSE, VERBOSE)
+SC_LOG_IMP (INFO, INFO)
+SC_LOG_IMP (STATISTICS, STATISTICS)
+SC_LOG_IMP (PRODUCTION, PRODUCTION)
+SC_LOG_IMP (ESSENTIAL, ESSENTIAL)
+SC_LOG_IMP (LERROR, ERROR)
+/* *INDENT-ON* */
+
+#endif
diff --git a/sc/src/sc.h b/sc/src/sc.h
new file mode 100644
index 0000000..b87673d
--- /dev/null
+++ b/sc/src/sc.h
@@ -0,0 +1,567 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+/** \file sc.h
+ *
+ * Support for process management (memory allocation, logging, etc.)
+ */
+
+/** \defgroup sc libsc
+ *
+ * The SC Library provides support for parallel scientific applications.
+ */
+
+#ifndef SC_H
+#define SC_H
+
+/* include the sc_config header first */
+
+#include <sc_config.h>
+#ifndef _sc_const
+#define _sc_const const
+#endif
+#ifndef _sc_restrict
+#define _sc_restrict restrict
+#endif
+
+/* use this in case mpi.h includes stdint.h */
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+
+/* include MPI before stdio.h */
+
+#ifdef SC_ENABLE_MPI
+#include <mpi.h>
+#else
+#ifdef MPI_SUCCESS
+#error "mpi.h is included.  Use --enable-mpi."
+#endif
+#endif
+
+/* include system headers */
+
+#include <math.h>
+#include <ctype.h>
+#include <float.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* provide extern C defines */
+
+/* the hacks below enable semicolons after the SC_EXTERN_C_ macros */
+#ifdef __cplusplus
+#define SC_EXTERN_C_BEGIN       extern "C" { void sc_extern_c_hack_1 (void)
+#define SC_EXTERN_C_END                    } void sc_extern_c_hack_2 (void)
+#else
+#define SC_EXTERN_C_BEGIN                    void sc_extern_c_hack_3 (void)
+#define SC_EXTERN_C_END                      void sc_extern_c_hack_4 (void)
+#endif
+
+/* this libsc header is always included */
+#include <sc_mpi.h>
+
+SC_EXTERN_C_BEGIN;
+
+/* extern variables */
+
+extern const int    sc_log2_lookup_table[256];
+extern int          sc_package_id;
+
+/* control a trace file by environment variables (see sc_init) */
+extern FILE        *sc_trace_file;
+extern int          sc_trace_prio;
+
+/* define math constants if necessary */
+#ifndef M_E
+#define M_E 2.7182818284590452354       /* e */
+#endif
+#ifndef M_LOG2E
+#define M_LOG2E 1.4426950408889634074   /* log_2 e */
+#endif
+#ifndef M_LOG10E
+#define M_LOG10E 0.43429448190325182765 /* log_10 e */
+#endif
+#ifndef M_LN2
+#define M_LN2 0.69314718055994530942    /* log_e 2 */
+#endif
+#ifndef M_LN10
+#define M_LN10 2.30258509299404568402   /* log_e 10 */
+#endif
+#ifndef M_PI
+#define M_PI 3.14159265358979323846     /* pi */
+#endif
+#ifndef M_PI_2
+#define M_PI_2 1.57079632679489661923   /* pi/2 */
+#endif
+#ifndef M_PI_4
+#define M_PI_4 0.78539816339744830962   /* pi/4 */
+#endif
+#ifndef M_1_PI
+#define M_1_PI 0.31830988618379067154   /* 1/pi */
+#endif
+#ifndef M_2_PI
+#define M_2_PI 0.63661977236758134308   /* 2/pi */
+#endif
+#ifndef M_2_SQRTPI
+#define M_2_SQRTPI 1.12837916709551257390       /* 2/sqrt(pi) */
+#endif
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880  /* sqrt(2) */
+#endif
+#ifndef M_SQRT1_2
+#define M_SQRT1_2 0.70710678118654752440        /* 1/sqrt(2) */
+#endif
+
+#define SC_EPS               2.220446049250313e-16
+#define SC_1000_EPS (1000. * 2.220446049250313e-16)
+
+#if 0
+/*@ignore@*/
+#define index   DONT_USE_NAME_CONFLICT_1 ---
+#define rindex  DONT_USE_NAME_CONFLICT_2 ---
+#define link    DONT_USE_NAME_CONFLICT_3 ---
+#define NO_DEFINE_DONT_USE_CONFLICT SPLINT_IS_STUPID_ALSO
+/*@end@*/
+#endif /* 0 */
+
+/* check macros, always enabled */
+
+#define SC_NOOP() ((void) (0))
+#define SC_ABORT(s)                             \
+  sc_abort_verbose (__FILE__, __LINE__, (s))
+#define SC_ABORT_NOT_REACHED() SC_ABORT ("Unreachable code")
+#define SC_CHECK_ABORT(q,s)                     \
+  ((q) ? (void) 0 : SC_ABORT (s))
+#define SC_CHECK_MPI(r) SC_CHECK_ABORT ((r) == sc_MPI_SUCCESS, "MPI error")
+#define SC_CHECK_ZLIB(r) SC_CHECK_ABORT ((r) == Z_OK, "zlib error")
+
+/*
+ * C++98 does not allow variadic macros
+ * 1. Declare a default variadic function for C and C++
+ * 2. Use macros in C instead of the function
+ * This loses __FILE__ and __LINE__ in the C++ ..F log functions
+ */
+void                SC_ABORTF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)))
+  __attribute__ ((noreturn));
+void                SC_CHECK_ABORTF (int success, const char *fmt, ...)
+  __attribute__ ((format (printf, 2, 3)));
+#ifndef __cplusplus
+#define SC_ABORTF(fmt,...)                                      \
+  sc_abort_verbosef (__FILE__, __LINE__, (fmt), __VA_ARGS__)
+#define SC_CHECK_ABORTF(q,fmt,...)                      \
+  ((q) ? (void) 0 : SC_ABORTF (fmt, __VA_ARGS__))
+#endif
+#define SC_ABORT1(fmt,a)                                \
+  sc_abort_verbosef (__FILE__, __LINE__, (fmt), (a))
+#define SC_ABORT2(fmt,a,b)                                      \
+  sc_abort_verbosef (__FILE__, __LINE__, (fmt), (a), (b))
+#define SC_ABORT3(fmt,a,b,c)                                    \
+  sc_abort_verbosef (__FILE__, __LINE__, (fmt), (a), (b), (c))
+#define SC_ABORT4(fmt,a,b,c,d)                                          \
+  sc_abort_verbosef (__FILE__, __LINE__, (fmt), (a), (b), (c), (d))
+#define SC_ABORT5(fmt,a,b,c,d,e)                                        \
+  sc_abort_verbosef (__FILE__, __LINE__, (fmt), (a), (b), (c), (d), (e))
+#define SC_ABORT6(fmt,a,b,c,d,e,f)                                      \
+  sc_abort_verbosef (__FILE__, __LINE__, (fmt), (a), (b), (c), (d), (e), (f))
+#define SC_CHECK_ABORT1(q,fmt,a)                \
+  ((q) ? (void) 0 : SC_ABORT1 ((fmt), (a)))
+#define SC_CHECK_ABORT2(q,fmt,a,b)                      \
+  ((q) ? (void) 0 : SC_ABORT2 ((fmt), (a), (b)))
+#define SC_CHECK_ABORT3(q,fmt,a,b,c)                    \
+  ((q) ? (void) 0 : SC_ABORT3 ((fmt), (a), (b), (c)))
+#define SC_CHECK_ABORT4(q,fmt,a,b,c,d)                          \
+  ((q) ? (void) 0 : SC_ABORT4 ((fmt), (a), (b), (c), (d)))
+#define SC_CHECK_ABORT5(q,fmt,a,b,c,d,e)                        \
+  ((q) ? (void) 0 : SC_ABORT5 ((fmt), (a), (b), (c), (d), (e)))
+#define SC_CHECK_ABORT6(q,fmt,a,b,c,d,e,f)                              \
+  ((q) ? (void) 0 : SC_ABORT6 ((fmt), (a), (b), (c), (d), (e), (f)))
+
+/* assertions, only enabled in debug mode */
+
+#ifdef SC_DEBUG
+#define SC_ASSERT(c) SC_CHECK_ABORT ((c), "Assertion '" #c "'")
+#define SC_EXECUTE_ASSERT_FALSE(expression)                             \
+  do { int _sc_i = (int) (expression);                                  \
+       SC_CHECK_ABORT (!_sc_i, "Expected false: '" #expression "'");    \
+  } while (0)
+#define SC_EXECUTE_ASSERT_TRUE(expression)                              \
+  do { int _sc_i = (int) (expression);                                  \
+       SC_CHECK_ABORT (_sc_i, "Expected true: '" #expression "'");      \
+  } while (0)
+#else
+#define SC_ASSERT(c) SC_NOOP ()
+#define SC_EXECUTE_ASSERT_FALSE(expression) \
+  do { (void) (expression); } while (0)
+#define SC_EXECUTE_ASSERT_TRUE(expression) \
+  do { (void) (expression); } while (0)
+#endif
+
+/* macros for memory allocation, will abort if out of memory */
+
+#define SC_ALLOC(t,n)         (t *) sc_malloc (sc_package_id, (n) * sizeof(t))
+#define SC_ALLOC_ZERO(t,n)    (t *) sc_calloc (sc_package_id, \
+                                               (size_t) (n), sizeof(t))
+#define SC_REALLOC(p,t,n)     (t *) sc_realloc (sc_package_id,          \
+                                             (p), (n) * sizeof(t))
+#define SC_STRDUP(s)                sc_strdup (sc_package_id, (s))
+#define SC_FREE(p)                  sc_free (sc_package_id, (p))
+
+/**
+ * Sets n elements of a memory range to zero.
+ * Assumes the pointer p is of the correct type.
+ */
+#define SC_BZERO(p,n) ((void) memset ((p), 0, (n) * sizeof (*(p))))
+
+/* min, max and square helper macros */
+
+#define SC_MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define SC_MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define SC_SQR(a) ((a) * (a))
+
+/* hopefully fast binary logarithms and binary round up */
+
+#define SC_LOG2_8(x) (sc_log2_lookup_table[(x)])
+#define SC_LOG2_16(x) (((x) > 0xff) ?                                   \
+                       (SC_LOG2_8 ((x) >> 8) + 8) : SC_LOG2_8 (x))
+#define SC_LOG2_32(x) (((x) > 0xffff) ?                                 \
+                       (SC_LOG2_16 ((x) >> 16)) + 16 : SC_LOG2_16 (x))
+#define SC_LOG2_64(x) (((x) > 0xffffffffLL) ?                           \
+                       (SC_LOG2_32 ((x) >> 32)) + 32 : SC_LOG2_32 (x))
+#define SC_ROUNDUP2_32(x)                               \
+  (((x) <= 0) ? 0 : (1 << (SC_LOG2_32 ((x) - 1) + 1)))
+#define SC_ROUNDUP2_64(x)                               \
+  (((x) <= 0) ? 0 : (1LL << (SC_LOG2_64 ((x) - 1LL) + 1)))
+
+/* log categories */
+
+#define SC_LC_GLOBAL      1     /**< log only for master process */
+#define SC_LC_NORMAL      2     /**< log for every process */
+
+/** \defgroup logpriorities log priorities
+ *
+ * Numbers designating the level of logging output.
+ *
+ * Priorities TRACE to VERBOSE are appropriate when all parallel processes
+ * contribute log messages.  INFO and above must not clutter the output of
+ * large parallel runs.  STATISTICS can be used for important measurements.
+ * PRODUCTION is meant for rudimentary information on the program flow.
+ * ESSENTIAL can be used for one-time messages, say at program startup.
+ *
+ * \ingroup sc
+ */
+/*@{ \ingroup logpriorities */
+/* log priorities */
+#define SC_LP_DEFAULT   (-1)    /**< this selects the SC default threshold */
+#define SC_LP_ALWAYS      0     /**< this will log everything */
+#define SC_LP_TRACE       1     /**< this will prefix file and line number */
+#define SC_LP_DEBUG       2     /**< any information on the internal state */
+#define SC_LP_VERBOSE     3     /**< information on conditions, decisions */
+#define SC_LP_INFO        4     /**< the main things a function is doing */
+#define SC_LP_STATISTICS  5     /**< important for consistency/performance */
+#define SC_LP_PRODUCTION  6     /**< a few lines for a major api function */
+#define SC_LP_ESSENTIAL   7     /**< this logs a few lines max per program */
+#define SC_LP_ERROR       8     /**< this logs errors only */
+#define SC_LP_SILENT      9     /**< this never logs anything */
+/*@}*/
+
+/** The log priority for the sc package.
+ *
+ */
+#ifdef SC_LOG_PRIORITY
+#define SC_LP_THRESHOLD SC_LOG_PRIORITY
+#else
+#ifdef SC_DEBUG
+#define SC_LP_THRESHOLD SC_LP_TRACE
+#else
+#define SC_LP_THRESHOLD SC_LP_INFO
+#endif
+#endif
+
+/* generic log macros */
+#define SC_GEN_LOG(package,category,priority,s)                         \
+  ((priority) < SC_LP_THRESHOLD ? (void) 0 :                            \
+   sc_log (__FILE__, __LINE__, (package), (category), (priority), (s)))
+#define SC_GLOBAL_LOG(p,s) SC_GEN_LOG (sc_package_id, SC_LC_GLOBAL, (p), (s))
+#define SC_LOG(p,s) SC_GEN_LOG (sc_package_id, SC_LC_NORMAL, (p), (s))
+void                SC_GEN_LOGF (int package, int category, int priority,
+                                 const char *fmt, ...)
+  __attribute__ ((format (printf, 4, 5)));
+void                SC_GLOBAL_LOGF (int priority, const char *fmt, ...)
+  __attribute__ ((format (printf, 2, 3)));
+void                SC_LOGF (int priority, const char *fmt, ...)
+  __attribute__ ((format (printf, 2, 3)));
+#ifndef __cplusplus
+#define SC_GEN_LOGF(package,category,priority,fmt,...)                  \
+  ((priority) < SC_LP_THRESHOLD ? (void) 0 :                            \
+   sc_logf (__FILE__, __LINE__, (package), (category), (priority),      \
+            (fmt), __VA_ARGS__))
+#define SC_GLOBAL_LOGF(p,fmt,...)                                       \
+  SC_GEN_LOGF (sc_package_id, SC_LC_GLOBAL, (p), (fmt), __VA_ARGS__)
+#define SC_LOGF(p,fmt,...)                                              \
+  SC_GEN_LOGF (sc_package_id, SC_LC_NORMAL, (p), (fmt), __VA_ARGS__)
+#endif
+
+/* convenience global log macros will only output if identifier <= 0 */
+#define SC_GLOBAL_TRACE(s) SC_GLOBAL_LOG (SC_LP_TRACE, (s))
+#define SC_GLOBAL_LDEBUG(s) SC_GLOBAL_LOG (SC_LP_DEBUG, (s))
+#define SC_GLOBAL_VERBOSE(s) SC_GLOBAL_LOG (SC_LP_VERBOSE, (s))
+#define SC_GLOBAL_INFO(s) SC_GLOBAL_LOG (SC_LP_INFO, (s))
+#define SC_GLOBAL_STATISTICS(s) SC_GLOBAL_LOG (SC_LP_STATISTICS, (s))
+#define SC_GLOBAL_PRODUCTION(s) SC_GLOBAL_LOG (SC_LP_PRODUCTION, (s))
+#define SC_GLOBAL_ESSENTIAL(s) SC_GLOBAL_LOG (SC_LP_ESSENTIAL, (s))
+#define SC_GLOBAL_LERROR(s) SC_GLOBAL_LOG (SC_LP_ERROR, (s))
+void                SC_GLOBAL_TRACEF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_GLOBAL_LDEBUGF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_GLOBAL_VERBOSEF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_GLOBAL_INFOF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_GLOBAL_STATISTICSF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_GLOBAL_PRODUCTIONF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_GLOBAL_ESSENTIALF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_GLOBAL_LERRORF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+#ifndef __cplusplus
+#define SC_GLOBAL_TRACEF(fmt,...)                       \
+  SC_GLOBAL_LOGF (SC_LP_TRACE, (fmt), __VA_ARGS__)
+#define SC_GLOBAL_LDEBUGF(fmt,...)                      \
+  SC_GLOBAL_LOGF (SC_LP_DEBUG, (fmt), __VA_ARGS__)
+#define SC_GLOBAL_VERBOSEF(fmt,...)                     \
+  SC_GLOBAL_LOGF (SC_LP_VERBOSE, (fmt), __VA_ARGS__)
+#define SC_GLOBAL_INFOF(fmt,...)                        \
+  SC_GLOBAL_LOGF (SC_LP_INFO, (fmt), __VA_ARGS__)
+#define SC_GLOBAL_STATISTICSF(fmt,...)                  \
+  SC_GLOBAL_LOGF (SC_LP_STATISTICS, (fmt), __VA_ARGS__)
+#define SC_GLOBAL_PRODUCTIONF(fmt,...)                  \
+  SC_GLOBAL_LOGF (SC_LP_PRODUCTION, (fmt), __VA_ARGS__)
+#define SC_GLOBAL_ESSENTIALF(fmt,...)                   \
+  SC_GLOBAL_LOGF (SC_LP_ESSENTIAL, (fmt), __VA_ARGS__)
+#define SC_GLOBAL_LERRORF(fmt,...)                      \
+  SC_GLOBAL_LOGF (SC_LP_ERROR, (fmt), __VA_ARGS__)
+#endif
+
+/* convenience log macros that output regardless of identifier */
+#define SC_TRACE(s) SC_LOG (SC_LP_TRACE, (s))
+#define SC_LDEBUG(s) SC_LOG (SC_LP_DEBUG, (s))
+#define SC_VERBOSE(s) SC_LOG (SC_LP_VERBOSE, (s))
+#define SC_INFO(s) SC_LOG (SC_LP_INFO, (s))
+#define SC_STATISTICS(s) SC_LOG (SC_LP_STATISTICS, (s))
+#define SC_PRODUCTION(s) SC_LOG (SC_LP_PRODUCTION, (s))
+#define SC_ESSENTIAL(s) SC_LOG (SC_LP_ESSENTIAL, (s))
+#define SC_LERROR(s) SC_LOG (SC_LP_ERROR, (s))
+void                SC_TRACEF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_LDEBUGF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_VERBOSEF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_INFOF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_STATISTICSF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_PRODUCTIONF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_ESSENTIALF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                SC_LERRORF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+#ifndef __cplusplus
+#define SC_TRACEF(fmt,...)                      \
+  SC_LOGF (SC_LP_TRACE, (fmt), __VA_ARGS__)
+#define SC_LDEBUGF(fmt,...)                     \
+  SC_LOGF (SC_LP_DEBUG, (fmt), __VA_ARGS__)
+#define SC_VERBOSEF(fmt,...)                    \
+  SC_LOGF (SC_LP_VERBOSE, (fmt), __VA_ARGS__)
+#define SC_INFOF(fmt,...)                       \
+  SC_LOGF (SC_LP_INFO, (fmt), __VA_ARGS__)
+#define SC_STATISTICSF(fmt,...)                         \
+  SC_LOGF (SC_LP_STATISTICS, (fmt), __VA_ARGS__)
+#define SC_PRODUCTIONF(fmt,...)                         \
+  SC_LOGF (SC_LP_PRODUCTION, (fmt), __VA_ARGS__)
+#define SC_ESSENTIALF(fmt,...)                  \
+  SC_LOGF (SC_LP_ESSENTIAL, (fmt), __VA_ARGS__)
+#define SC_LERRORF(fmt,...)                     \
+  SC_LOGF (SC_LP_ERROR, (fmt), __VA_ARGS__)
+#endif
+
+/* callback typedefs */
+
+typedef void        (*sc_handler_t) (void *data);
+typedef void        (*sc_log_handler_t) (FILE * log_stream,
+                                         const char *filename, int lineno,
+                                         int package, int category,
+                                         int priority, const char *msg);
+
+/* memory allocation functions, will abort if out of memory */
+
+void               *sc_malloc (int package, size_t size);
+void               *sc_calloc (int package, size_t nmemb, size_t size);
+void               *sc_realloc (int package, void *ptr, size_t size);
+char               *sc_strdup (int package, const char *s);
+void                sc_free (int package, void *ptr);
+int                 sc_memory_status (int package);
+void                sc_memory_check (int package);
+
+/* comparison functions for various integer sizes */
+
+int                 sc_int_compare (const void *v1, const void *v2);
+int                 sc_int8_compare (const void *v1, const void *v2);
+int                 sc_int16_compare (const void *v1, const void *v2);
+int                 sc_int32_compare (const void *v1, const void *v2);
+int                 sc_int64_compare (const void *v1, const void *v2);
+int                 sc_double_compare (const void *v1, const void *v2);
+
+/** Controls the default SC log behavior.
+ * \param [in] log_stream    Set stream to use by sc_logf (or NULL for stdout).
+ * \param [in] log_handler   Set default SC log handler (NULL selects builtin).
+ * \param [in] log_threshold Set default SC log threshold (or SC_LP_DEFAULT).
+ *                           May be SC_LP_ALWAYS or SC_LP_SILENT.
+ */
+void                sc_set_log_defaults (FILE * log_stream,
+                                         sc_log_handler_t log_handler,
+                                         int log_thresold);
+
+/** The central log function to be called by all packages.
+ * Dispatches the log calls by package and filters by category and priority.
+ * \param [in] package   Must be a registered package id or -1.
+ * \param [in] category  Must be SC_LC_NORMAL or SC_LC_GLOBAL.
+ * \param [in] priority  Must be > SC_LP_ALWAYS and < SC_LP_SILENT.
+ */
+void                sc_log (const char *filename, int lineno,
+                            int package, int category, int priority,
+                            const char *msg);
+void                sc_logf (const char *filename, int lineno,
+                             int package, int category, int priority,
+                             const char *fmt, ...)
+  __attribute__ ((format (printf, 6, 7)));
+void                sc_logv (const char *filename, int lineno,
+                             int package, int category, int priority,
+                             const char *fmt, va_list ap);
+
+/** Add spaces to the start of a package's default log format. */
+void                sc_log_indent_push_count (int package, int count);
+
+/** Remove spaces from the start of a package's default log format. */
+void                sc_log_indent_pop_count (int package, int count);
+
+/** Add one space to the start of sc's default log format. */
+void                sc_log_indent_push (void);
+
+/** Remove one space from the start of a sc's default log format. */
+void                sc_log_indent_pop (void);
+
+/** Print a stack trace, call the abort handler and then call abort (). */
+void                sc_abort (void)
+  __attribute__ ((noreturn));
+
+/** Print a message to stderr and then call sc_abort (). */
+void                sc_abort_verbose (const char *filename, int lineno,
+                                      const char *msg)
+  __attribute__ ((noreturn));
+
+/** Print a message to stderr and then call sc_abort (). */
+void                sc_abort_verbosef (const char *filename, int lineno,
+                                       const char *fmt, ...)
+  __attribute__ ((format (printf, 3, 4)))
+  __attribute__ ((noreturn));
+
+/** Print a message to stderr and then call sc_abort (). */
+void                sc_abort_verbosev (const char *filename, int lineno,
+                                       const char *fmt, va_list ap)
+  __attribute__ ((noreturn));
+
+/** Collective abort where only root prints a message */
+void                sc_abort_collective (const char *msg)
+  __attribute__ ((noreturn));
+
+/** Register a software package with SC.
+ * This function must only be called before additional threads are created.
+ * The logging parameters are as in sc_set_log_defaults.
+ * \return                   Returns a unique package id.
+ */
+int                 sc_package_register (sc_log_handler_t log_handler,
+                                         int log_threshold,
+                                         const char *name, const char *full);
+int                 sc_package_is_registered (int package_id);
+
+/** Unregister a software package with SC.
+ * This function must only be called after additional threads are finished.
+ */
+void                sc_package_unregister (int package_id);
+
+/** Print a summary of all packages registered with SC.
+ * Uses the SC_LP_GLOBAL log category which by default only prints on rank 0.
+ * \param [in] log_priority     Priority passed to sc log functions.
+ */
+void                sc_package_print_summary (int log_priority);
+
+/** Sets the global program identifier (e.g. the MPI rank) and some flags.
+ * This function is optional.
+ * This function must only be called before additional threads are created.
+ * If this function is not called or called with log_handler == NULL,
+ * the default SC log handler will be used.
+ * If this function is not called or called with log_threshold == SC_LP_DEFAULT,
+ * the default SC log threshold will be used.
+ * The default SC log settings can be changed with sc_set_log_defaults ().
+ * \param [in] mpicomm          MPI communicator, can be sc_MPI_COMM_NULL.
+ *                              If sc_MPI_COMM_NULL, the identifier is set to -1.
+ *                              Otherwise, sc_MPI_Init must have been called.
+ * \param [in] catch_signals    If true, signals INT SEGV USR2 are be caught.
+ * \param [in] print_backtrace  If true, sc_abort prints a backtrace.
+ */
+void                sc_init (sc_MPI_Comm mpicomm,
+                             int catch_signals, int print_backtrace,
+                             sc_log_handler_t log_handler, int log_threshold);
+
+/** Unregisters all packages, runs the memory check, removes the
+ * signal handlers and resets sc_identifier and sc_root_*.
+ * This function is optional.
+ * This function does not require sc_init to be called first.
+ */
+void                sc_finalize (void);
+
+/** Identify the root process.
+ * Only meaningful between sc_init and sc_finalize and
+ * with a communicator that is not sc_MPI_COMM_NULL (otherwise always true).
+ *
+ * \return          Return true for the root process and false otherwise.
+ */
+int                 sc_is_root (void);
+
+SC_EXTERN_C_END;
+
+#endif /* SC_H */
diff --git a/sc/src/sc_allgather.c b/sc/src/sc_allgather.c
new file mode 100644
index 0000000..bbeaf9c
--- /dev/null
+++ b/sc/src/sc_allgather.c
@@ -0,0 +1,166 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_allgather.h>
+
+void
+sc_allgather_alltoall (sc_MPI_Comm mpicomm, char *data, int datasize,
+                       int groupsize, int myoffset, int myrank)
+{
+  int                 j, peer;
+  int                 mpiret;
+  sc_MPI_Request     *request;
+
+  SC_ASSERT (myoffset >= 0 && myoffset < groupsize);
+
+  request = SC_ALLOC (sc_MPI_Request, 2 * groupsize);
+
+  for (j = 0; j < groupsize; ++j) {
+    if (j == myoffset) {
+      request[j] = request[groupsize + j] = sc_MPI_REQUEST_NULL;
+      continue;
+    }
+    peer = myrank - (myoffset - j);
+
+    mpiret = sc_MPI_Irecv (data + j * datasize, datasize, sc_MPI_BYTE,
+                           peer, SC_TAG_AG_ALLTOALL, mpicomm, request + j);
+    SC_CHECK_MPI (mpiret);
+
+    mpiret = sc_MPI_Isend (data + myoffset * datasize, datasize, sc_MPI_BYTE,
+                           peer, SC_TAG_AG_ALLTOALL,
+                           mpicomm, request + groupsize + j);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  mpiret = sc_MPI_Waitall (2 * groupsize, request, sc_MPI_STATUSES_IGNORE);
+  SC_CHECK_MPI (mpiret);
+
+  SC_FREE (request);
+}
+
+void
+sc_allgather_recursive (sc_MPI_Comm mpicomm, char *data, int datasize,
+                        int groupsize, int myoffset, int myrank)
+{
+  const int           g2 = groupsize / 2;
+  const int           g2B = groupsize - g2;
+  int                 mpiret;
+  sc_MPI_Request      request[3];
+
+  SC_ASSERT (myoffset >= 0 && myoffset < groupsize);
+
+  if (groupsize > SC_AG_ALLTOALL_MAX) {
+    if (myoffset < g2) {
+      sc_allgather_recursive (mpicomm, data, datasize, g2, myoffset, myrank);
+
+      mpiret = sc_MPI_Irecv (data + g2 * datasize, g2B * datasize,
+                             sc_MPI_BYTE, myrank + g2, SC_TAG_AG_RECURSIVE_B,
+                             mpicomm, request + 0);
+      SC_CHECK_MPI (mpiret);
+
+      mpiret = sc_MPI_Isend (data, g2 * datasize, sc_MPI_BYTE,
+                             myrank + g2, SC_TAG_AG_RECURSIVE_A,
+                             mpicomm, request + 1);
+      SC_CHECK_MPI (mpiret);
+
+      if (myoffset == g2 - 1 && g2 != g2B) {
+        mpiret = sc_MPI_Isend (data, g2 * datasize, sc_MPI_BYTE,
+                               myrank + g2B, SC_TAG_AG_RECURSIVE_C,
+                               mpicomm, request + 2);
+        SC_CHECK_MPI (mpiret);
+      }
+      else {
+        request[2] = sc_MPI_REQUEST_NULL;
+      }
+    }
+    else {
+      sc_allgather_recursive (mpicomm, data + g2 * datasize, datasize, g2B,
+                              myoffset - g2, myrank);
+
+      if (myoffset == groupsize - 1 && g2 != g2B) {
+        request[0] = sc_MPI_REQUEST_NULL;
+        request[1] = sc_MPI_REQUEST_NULL;
+
+        mpiret = sc_MPI_Irecv (data, g2 * datasize, sc_MPI_BYTE,
+                               myrank - g2B, SC_TAG_AG_RECURSIVE_C,
+                               mpicomm, request + 2);
+        SC_CHECK_MPI (mpiret);
+      }
+      else {
+        mpiret = sc_MPI_Irecv (data, g2 * datasize, sc_MPI_BYTE,
+                               myrank - g2, SC_TAG_AG_RECURSIVE_A,
+                               mpicomm, request + 0);
+        SC_CHECK_MPI (mpiret);
+
+        mpiret = sc_MPI_Isend (data + g2 * datasize, g2B * datasize,
+                               sc_MPI_BYTE, myrank - g2,
+                               SC_TAG_AG_RECURSIVE_B, mpicomm, request + 1);
+        SC_CHECK_MPI (mpiret);
+
+        request[2] = sc_MPI_REQUEST_NULL;
+      }
+    }
+
+    mpiret = sc_MPI_Waitall (3, request, sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+  else {
+    sc_allgather_alltoall (mpicomm, data, datasize, groupsize, myoffset,
+                           myrank);
+  }
+}
+
+int
+sc_allgather (void *sendbuf, int sendcount, sc_MPI_Datatype sendtype,
+              void *recvbuf, int recvcount, sc_MPI_Datatype recvtype,
+              sc_MPI_Comm mpicomm)
+{
+  int                 mpiret;
+  int                 mpisize;
+  int                 mpirank;
+  size_t              datasize;
+#ifdef SC_DEBUG
+  size_t              datasize2;
+#endif
+
+  SC_ASSERT (sendcount >= 0 && recvcount >= 0);
+
+  /* *INDENT-OFF* HORRIBLE indent bug */
+  datasize = (size_t) sendcount * sc_mpi_sizeof (sendtype);
+#ifdef SC_DEBUG
+  datasize2 = (size_t) recvcount * sc_mpi_sizeof (recvtype);
+#endif
+  /* *INDENT-ON* */
+
+  SC_ASSERT (datasize == datasize2);
+
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  memcpy (((char *) recvbuf) + mpirank * datasize, sendbuf, datasize);
+  sc_allgather_recursive (mpicomm, (char *) recvbuf, (int) datasize,
+                          mpisize, mpirank, mpirank);
+
+  return sc_MPI_SUCCESS;
+}
diff --git a/sc/src/sc_allgather.h b/sc/src/sc_allgather.h
new file mode 100644
index 0000000..bd1d5a1
--- /dev/null
+++ b/sc/src/sc_allgather.h
@@ -0,0 +1,57 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_ALLGATHER_H
+#define SC_ALLGATHER_H
+
+#include <sc.h>
+
+#ifndef SC_AG_ALLTOALL_MAX
+#define SC_AG_ALLTOALL_MAX      5
+#endif
+
+SC_EXTERN_C_BEGIN;
+
+/** Allgather by direct point-to-point communication.
+ * Only makes sense for small group sizes.
+ */
+void                sc_allgather_alltoall (sc_MPI_Comm mpicomm, char *data,
+                                           int datasize, int groupsize,
+                                           int myoffset, int myrank);
+
+/** Performs recursive bisection allgather.
+ * When size becomes small enough, calls sc_ag_alltoall.
+ */
+void                sc_allgather_recursive (sc_MPI_Comm mpicomm, char *data,
+                                            int datasize, int groupsize,
+                                            int myoffset, int myrank);
+
+/** Drop-in allgather replacement.
+ */
+int                 sc_allgather (void *sendbuf, int sendcount,
+                                  sc_MPI_Datatype sendtype, void *recvbuf,
+                                  int recvcount, sc_MPI_Datatype recvtype,
+                                  sc_MPI_Comm mpicomm);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_ALLGATHER_H */
diff --git a/sc/src/sc_amr.c b/sc/src/sc_amr.c
new file mode 100644
index 0000000..a0346f3
--- /dev/null
+++ b/sc/src/sc_amr.c
@@ -0,0 +1,293 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_amr.h>
+
+void
+sc_amr_error_stats (sc_MPI_Comm mpicomm, long num_elements,
+                    const double *errors, sc_amr_control_t * amr)
+{
+  sc_statinfo_t      *si = &amr->estats;
+  int                 mpiret;
+  int                 mpisize;
+  long                i;
+  double              sum, squares, emin, emax;
+
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+
+  amr->errors = errors;
+
+  sum = squares = 0.;
+  emin = DBL_MAX;
+  emax = -DBL_MAX;
+  for (i = 0; i < num_elements; ++i) {
+    sum += errors[i];
+    squares += errors[i] * errors[i];
+    emin = SC_MIN (emin, errors[i]);
+    emax = SC_MAX (emax, errors[i]);
+  }
+  si->dirty = 1;
+  si->count = num_elements;
+  si->sum_values = sum;
+  si->sum_squares = squares;
+  si->min = emin;
+  si->max = emax;
+  si->variable = NULL;
+  sc_stats_compute (mpicomm, 1, si);
+
+  amr->mpicomm = mpicomm;
+  amr->num_procs_long = (long) mpisize;
+  amr->num_total_estimated = amr->num_total_elements = si->count;
+  amr->coarsen_threshold = si->min;
+  amr->refine_threshold = si->max;
+  amr->num_total_coarsen = amr->num_total_refine = 0;
+}
+
+void
+sc_amr_coarsen_specify (int package_id,
+                        sc_amr_control_t * amr, double coarsen_threshold,
+                        sc_amr_count_coarsen_fn cfn, void *user_data)
+{
+  int                 mpiret;
+  long                local_coarsen, global_coarsen;
+
+  if (cfn == NULL) {
+    amr->coarsen_threshold = amr->estats.min;
+    local_coarsen = global_coarsen = 0;
+  }
+  else {
+    amr->coarsen_threshold = coarsen_threshold;
+    SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+                 "Set coarsen threshold %g assuming %ld refinements\n",
+                 amr->coarsen_threshold, amr->num_total_refine);
+
+    local_coarsen = cfn (amr, user_data);
+    mpiret = sc_MPI_Allreduce (&local_coarsen, &global_coarsen, 1,
+                               sc_MPI_LONG, sc_MPI_SUM, amr->mpicomm);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  amr->num_total_coarsen = global_coarsen;
+  amr->num_total_estimated =
+    amr->num_total_elements + amr->num_total_refine - global_coarsen;
+
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+               "Global number of coarsenings = %ld\n",
+               amr->num_total_coarsen);
+}
+
+void
+sc_amr_coarsen_search (int package_id, sc_amr_control_t * amr,
+                       long num_total_low, double coarsen_threshold_high,
+                       double target_window, int max_binary_steps,
+                       sc_amr_count_coarsen_fn cfn, void *user_data)
+{
+  const sc_statinfo_t *errors = &amr->estats;
+  const long          num_total_elements = amr->num_total_elements;
+  const long          num_total_refine = amr->num_total_refine;
+  int                 mpiret;
+  int                 binary_count;
+  long                local_coarsen, global_coarsen;
+  long                num_total_high, num_total_estimated;
+  double              coarsen_threshold_low;
+
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+               "Search for coarsen threshold assuming %ld refinements\n",
+               num_total_refine);
+
+  /* assign initial threshold range and check */
+  coarsen_threshold_low = errors->min;
+  if (cfn == NULL ||
+      coarsen_threshold_low >= coarsen_threshold_high ||
+      num_total_elements + num_total_refine <= num_total_low) {
+
+    SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+                 "Search for coarsening skipped with low = %g, up = %g\n",
+                 coarsen_threshold_low, coarsen_threshold_high);
+
+    amr->coarsen_threshold = errors->min;
+    amr->num_total_coarsen = 0;
+    amr->num_total_estimated = num_total_elements + num_total_refine;
+    return;
+  }
+
+  /* fix range of acceptable total element counts */
+  num_total_high = (long) (num_total_low / target_window);
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_INFO,
+               "Range of acceptable total element counts %ld %ld\n",
+               num_total_low, num_total_high);
+
+  /* start binary search at the upper end */
+  amr->coarsen_threshold = coarsen_threshold_high;
+  for (binary_count = 0;; ++binary_count) {
+
+    /* call back to count the elements to coarsen locally */
+    local_coarsen = cfn (amr, user_data);
+    mpiret = sc_MPI_Allreduce (&local_coarsen, &global_coarsen, 1,
+                               sc_MPI_LONG, sc_MPI_SUM, amr->mpicomm);
+    SC_CHECK_MPI (mpiret);
+    num_total_estimated =
+      num_total_elements + num_total_refine - global_coarsen;
+    SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+                 "At %g total %ld estimated %ld coarsen %ld\n",
+                 amr->coarsen_threshold, num_total_elements,
+                 num_total_estimated, global_coarsen);
+
+    /* check loop condition */
+    if (binary_count == max_binary_steps) {
+      break;
+    }
+
+    /* binary search action */
+    if (num_total_estimated < num_total_low) {
+      coarsen_threshold_high = amr->coarsen_threshold;
+    }
+    else if (num_total_estimated > num_total_high) {
+      if (binary_count == 0) {
+        break;                  /* impossible to coarsen more */
+      }
+      coarsen_threshold_low = amr->coarsen_threshold;
+    }
+    else {                      /* binary search sucessful */
+      break;
+    }
+    SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+                 "Binary search for %ld elements at low = %g, up = %g\n",
+                 num_total_low, coarsen_threshold_low,
+                 coarsen_threshold_high);
+
+    /* compute next guess for binary search */
+    amr->coarsen_threshold =
+      (coarsen_threshold_low + coarsen_threshold_high) / 2.;
+  }
+  amr->num_total_coarsen = global_coarsen;
+  amr->num_total_estimated = num_total_estimated;
+
+  /* binary search is ended */
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+               "Search for coarsen stopped after %d steps with threshold %g\n",
+               binary_count, amr->coarsen_threshold);
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+               "Global number of coarsenings = %ld\n",
+               amr->num_total_coarsen);
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_INFO,
+               "Estimated global number of elements = %ld\n",
+               amr->num_total_estimated);
+}
+
+void
+sc_amr_refine_search (int package_id, sc_amr_control_t * amr,
+                      long num_total_high, double refine_threshold_low,
+                      double target_window, int max_binary_steps,
+                      sc_amr_count_refine_fn rfn, void *user_data)
+{
+  const sc_statinfo_t *errors = &amr->estats;
+  const long          num_total_elements = amr->num_total_elements;
+  const long          num_total_coarsen = amr->num_total_coarsen;
+  int                 mpiret;
+  int                 binary_count;
+  long                local_refine, global_refine;
+  long                num_total_low, num_total_estimated;
+  double              refine_threshold_high;
+
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+               "Search for refine threshold assuming %ld coarsenings\n",
+               num_total_coarsen);
+
+  /* assign initial threshold range and check */
+  refine_threshold_high = errors->max;
+  if (rfn == NULL ||
+      refine_threshold_low >= refine_threshold_high ||
+      num_total_elements - num_total_coarsen >= num_total_high) {
+
+    SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+                 "Search for refinement skipped with low = %g, up = %g\n",
+                 refine_threshold_low, refine_threshold_high);
+
+    amr->refine_threshold = errors->max;
+    amr->num_total_refine = 0;
+    amr->num_total_estimated = num_total_elements - num_total_coarsen;
+    return;
+  }
+
+  /* fix range of acceptable total element counts */
+  num_total_low = (long) (num_total_high * target_window);
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_INFO,
+               "Range of acceptable total element counts %ld %ld\n",
+               num_total_low, num_total_high);
+
+  /* start binary search at the lower end */
+  amr->refine_threshold = refine_threshold_low;
+  for (binary_count = 0;; ++binary_count) {
+
+    /* call back to count the elements to refine locally */
+    local_refine = rfn (amr, user_data);
+    mpiret = sc_MPI_Allreduce (&local_refine, &global_refine, 1, sc_MPI_LONG,
+                               sc_MPI_SUM, amr->mpicomm);
+    SC_CHECK_MPI (mpiret);
+    num_total_estimated =
+      num_total_elements + global_refine - num_total_coarsen;
+    SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+                 "At %g total %ld estimated %ld refine %ld\n",
+                 amr->refine_threshold, num_total_elements,
+                 num_total_estimated, global_refine);
+
+    /* check loop condition */
+    if (binary_count == max_binary_steps) {
+      break;
+    }
+
+    /* binary search action */
+    if (num_total_estimated < num_total_low) {
+      if (binary_count == 0) {
+        break;                  /* impossible to refine more */
+      }
+      refine_threshold_high = amr->refine_threshold;
+    }
+    else if (num_total_estimated > num_total_high) {
+      refine_threshold_low = amr->refine_threshold;
+    }
+    else {                      /* binary search sucessful */
+      break;
+    }
+    SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+                 "Binary search for %ld elements at low = %g, up = %g\n",
+                 num_total_high, refine_threshold_low, refine_threshold_high);
+
+    /* compute next guess for binary search */
+    amr->refine_threshold =
+      (refine_threshold_low + refine_threshold_high) / 2.;
+  }
+  amr->num_total_refine = global_refine;
+  amr->num_total_estimated = num_total_estimated;
+
+  /* binary search is ended */
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+               "Search for refine stopped after %d steps with threshold %g\n",
+               binary_count, amr->refine_threshold);
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_STATISTICS,
+               "Global number of refinements = %ld\n", amr->num_total_refine);
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, SC_LP_INFO,
+               "Estimated global number of elements = %ld\n",
+               amr->num_total_estimated);
+}
diff --git a/sc/src/sc_amr.h b/sc/src/sc_amr.h
new file mode 100644
index 0000000..96d3bd2
--- /dev/null
+++ b/sc/src/sc_amr.h
@@ -0,0 +1,135 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_AMR_H
+#define SC_AMR_H
+
+#include <sc_statistics.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef struct sc_amr_control
+{
+  const double       *errors;
+  sc_statinfo_t       estats;
+  sc_MPI_Comm         mpicomm;
+  long                num_procs_long;
+  long                num_total_elements;
+  double              coarsen_threshold;
+  double              refine_threshold;
+  long                num_total_coarsen;
+  long                num_total_refine;
+  long                num_total_estimated;
+}
+sc_amr_control_t;
+
+/** Compute global error statistics.
+ * \param [in] mpicomm        MPI communicator to use.
+ * \param [in] mpisize        Number of MPI processes in this communicator.
+ * \param [in] num_local_elements   Number of local elements.
+ * \param [in] errors         The error values, one per local element.
+ * \param [out] amr           Structure will be initialized and estats filled.
+ */
+void                sc_amr_error_stats (sc_MPI_Comm mpicomm,
+                                        long num_local_elements,
+                                        const double *errors,
+                                        sc_amr_control_t * amr);
+
+/** Count the local number of elements that will be coarsened.
+ *
+ * This is all elements whose error is below threshold
+ * and where there are no other conditions preventing coarsening
+ * (such as not all siblings may be coarsened or are on another processor).
+ *
+ * \return          Returns the net loss of local elements.
+ */
+typedef long        (*sc_amr_count_coarsen_fn) (sc_amr_control_t * amr,
+                                                void *user_data);
+
+/** Count the local number of elements that will be refined.
+ *
+ * This is all elements whose error is above the threshold
+ * and where there are no other conditions preventing refinement
+ * (such as refinement would not make the element too small).
+ *
+ * \return          Returns the net gain of local elements.
+ */
+typedef long        (*sc_amr_count_refine_fn) (sc_amr_control_t * amr,
+                                               void *user_data);
+
+/** Specify a coarsening threshold and count the expected elements.
+ *
+ * \param [in] package_id               Registered package id or -1.
+ * \param [in,out] amr                  AMR control structure.
+ * \param [in] coarsen_threshold        Coarsening threshold.
+ * \param [in] cfn                      Callback to count local coarsenings.
+ * \param [in] user_data                Will be passed to the cfn callback.
+ */
+void                sc_amr_coarsen_specify (int package_id,
+                                            sc_amr_control_t * amr,
+                                            double coarsen_threshold,
+                                            sc_amr_count_coarsen_fn cfn,
+                                            void *user_data);
+
+/** Binary search for coarsening threshold without refinement.
+ *
+ * \param [in] package_id               Registered package id or -1.
+ * \param [in,out] amr                  AMR control structure.
+ * \param [in] num_total_ideal          Target number of global elements.
+ * \param [in] coarsen_threshold_high   Upper bound on the error indicator.
+ * \param [in] target_window            Relative target window (< 1).
+ * \param [in] max_binary_steps         Upper bound on binary search steps.
+ * \param [in] cfn                      Callback to count local coarsenings.
+ * \param [in] user_data                Will be passed to the cfn callback.
+ */
+void                sc_amr_coarsen_search (int package_id,
+                                           sc_amr_control_t * amr,
+                                           long num_total_ideal,
+                                           double coarsen_threshold_high,
+                                           double target_window,
+                                           int max_binary_steps,
+                                           sc_amr_count_coarsen_fn cfn,
+                                           void *user_data);
+
+/** Binary search for refinement threshold without coarsening.
+ *
+ * \param [in] package_id               Registered package id or -1.
+ * \param [in,out] amr                  AMR control structure.
+ * \param [in] num_total_ideal          Target number of global elements.
+ * \param [in] refine_threshold_low     Lower bound on the error indicator.
+ * \param [in] target_window            Relative target window (< 1).
+ * \param [in] max_binary_steps         Upper bound on binary search steps.
+ * \param [in] rfn                      Callback to count local refinements.
+ * \param [in] user_data                Will be passed to the rfn callback.
+ */
+void                sc_amr_refine_search (int package_id,
+                                          sc_amr_control_t * amr,
+                                          long num_total_ideal,
+                                          double refine_threshold_low,
+                                          double target_window,
+                                          int max_binary_steps,
+                                          sc_amr_count_refine_fn rfn,
+                                          void *user_data);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_AMR_H */
diff --git a/sc/src/sc_avl.c b/sc/src/sc_avl.c
new file mode 100644
index 0000000..d2f0c7f
--- /dev/null
+++ b/sc/src/sc_avl.c
@@ -0,0 +1,670 @@
+/* *INDENT-OFF* */
+
+/*****************************************************************************
+
+    avl.c - Source code for the AVL-tree library.
+
+    Copyright (C) 1998  Michael H. Buselli <cosine at cosine.org>
+    Copyright (C) 2000-2002  Wessel Dankers <wsl at nl.linux.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+
+    Augmented AVL-tree. Original by Michael H. Buselli <cosine at cosine.org>.
+
+    Modified by Wessel Dankers <wsl at nl.linux.org> to add a bunch of bloat to
+    the sourcecode, change the interface and squash a few bugs.
+    Mail him if you find new bugs.
+
+*****************************************************************************/
+
+/* renamed from avl.h to sc_avl.h for the SC Library and modified */
+
+#include <sc_avl.h>
+
+static void avl_rebalance(avl_tree_t *, avl_node_t *);
+
+#ifdef AVL_COUNT
+#define NODE_COUNT(n)  ((n) ? (n)->count : 0)
+#define L_COUNT(n)     (NODE_COUNT((n)->left))
+#define R_COUNT(n)     (NODE_COUNT((n)->right))
+#define CALC_COUNT(n)  (L_COUNT(n) + R_COUNT(n) + 1)
+#endif
+
+#ifdef AVL_DEPTH
+#define NODE_DEPTH(n)  ((n) ? (n)->depth : 0)
+#define L_DEPTH(n)     (NODE_DEPTH((n)->left))
+#define R_DEPTH(n)     (NODE_DEPTH((n)->right))
+#define CALC_DEPTH(n)  ((L_DEPTH(n)>R_DEPTH(n)?L_DEPTH(n):R_DEPTH(n)) + 1)
+#endif
+
+#ifndef AVL_DEPTH
+/* Also known as ffs() (from BSD) */
+static int lg(unsigned int u) {
+	int r = 1;
+	if(!u) return 0;
+	if(u & 0xffff0000) { u >>= 16; r += 16; }
+	if(u & 0x0000ff00) { u >>= 8; r += 8; }
+	if(u & 0x000000f0) { u >>= 4; r += 4; }
+	if(u & 0x0000000c) { u >>= 2; r += 2; }
+	if(u & 0x00000002) r++;
+	return r;
+}
+#endif
+
+static int avl_check_balance(avl_node_t *avlnode) {
+#ifdef AVL_DEPTH
+	int d;
+	d = R_DEPTH(avlnode) - L_DEPTH(avlnode);
+	return d<-1?-1:d>1?1:0;
+#else
+/*	int d;
+ *	d = lg(R_COUNT(avlnode)) - lg(L_COUNT(avlnode));
+ *	d = d<-1?-1:d>1?1:0;
+ */
+#ifdef AVL_COUNT
+	int pl;
+        unsigned r;
+
+	pl = lg(L_COUNT(avlnode));
+	r = R_COUNT(avlnode);
+
+	if(r>>(pl+1))
+		return 1;
+	if(pl<2 || r>>(pl-2))
+		return 0;
+	return -1;
+#else
+#error No balancing possible.
+#endif
+#endif
+}
+
+#ifdef AVL_COUNT
+unsigned int avl_count(const avl_tree_t *avltree) {
+	return NODE_COUNT(avltree->top);
+}
+
+avl_node_t *avl_at(const avl_tree_t *avltree, unsigned int u) {
+	avl_node_t *avlnode;
+	unsigned int c;
+
+	avlnode = avltree->top;
+
+	while(avlnode) {
+		c = L_COUNT(avlnode);
+
+		if(u < c) {
+			avlnode = avlnode->left;
+		} else if(u > c) {
+			avlnode = avlnode->right;
+			u -= c+1;
+		} else {
+			return avlnode;
+		}
+	}
+	return NULL;
+}
+
+unsigned int avl_index(const avl_node_t *avlnode) {
+	avl_node_t *next;
+	unsigned int c;
+
+	c = L_COUNT(avlnode);
+
+	while((next = avlnode->parent)) {
+		if(avlnode == next->right)
+			c += L_COUNT(next) + 1;
+		avlnode = next;
+	}
+
+	return c;
+}
+#endif
+
+int avl_search_closest(const avl_tree_t *avltree, const void *item, avl_node_t **avlnode) {
+	avl_node_t *node;
+	avl_compare_t cmp;
+	int c;
+
+	if(!avlnode)
+		avlnode = &node;
+
+	node = avltree->top;
+
+	if(!node)
+		return *avlnode = NULL, 0;
+
+	cmp = avltree->cmp;
+
+	for(;;) {
+		c = cmp(item, node->item);
+
+		if(c < 0) {
+			if(node->left)
+				node = node->left;
+			else
+				return *avlnode = node, -1;
+		} else if(c > 0) {
+			if(node->right)
+				node = node->right;
+			else
+				return *avlnode = node, 1;
+		} else {
+			return *avlnode = node, 0;
+		}
+	}
+}
+
+/*
+ * avl_search:
+ * Return a pointer to a node with the given item in the tree.
+ * If no such item is in the tree, then NULL is returned.
+ */
+avl_node_t *avl_search(const avl_tree_t *avltree, const void *item) {
+	avl_node_t *node;
+	return avl_search_closest(avltree, item, &node) ? NULL : node;
+}
+
+avl_tree_t *avl_init_tree(avl_tree_t *rc, avl_compare_t cmp, avl_freeitem_t freeitem) {
+	if(rc) {
+		rc->head = NULL;
+		rc->tail = NULL;
+		rc->top = NULL;
+		rc->cmp = cmp;
+		rc->freeitem = freeitem;
+	}
+	return rc;
+}
+
+avl_tree_t *avl_alloc_tree(avl_compare_t cmp, avl_freeitem_t freeitem) {
+        return avl_init_tree(SC_ALLOC(avl_tree_t, 1), cmp, freeitem);
+}
+
+void avl_clear_tree(avl_tree_t *avltree) {
+	avltree->top = avltree->head = avltree->tail = NULL;
+}
+
+void avl_free_nodes(avl_tree_t *avltree) {
+	avl_node_t *node, *next;
+	avl_freeitem_t freeitem;
+
+	freeitem = avltree->freeitem;
+
+	for(node = avltree->head; node; node = next) {
+		next = node->next;
+		if(freeitem)
+			freeitem(node->item);
+		SC_FREE(node);
+	}
+
+	avl_clear_tree(avltree);
+}
+
+/*
+ * avl_free_tree:
+ * Free all memory used by this tree.  If freeitem is not NULL, then
+ * it is assumed to be a destructor for the items referenced in the avl_
+ * tree, and they are deleted as well.
+ */
+void avl_free_tree(avl_tree_t *avltree) {
+	avl_free_nodes(avltree);
+	SC_FREE(avltree);
+}
+
+static void avl_clear_node(avl_node_t *newnode) {
+	newnode->left = newnode->right = NULL;
+	#ifdef AVL_COUNT
+	newnode->count = 1;
+	#endif
+	#ifdef AVL_DEPTH
+	newnode->depth = 1;
+	#endif
+}
+
+avl_node_t *avl_init_node(avl_node_t *newnode, void *item) {
+	if(newnode) {
+/*		avl_clear_node(newnode); */
+		newnode->item = item;
+	}
+	return newnode;
+}
+
+avl_node_t *avl_insert_top(avl_tree_t *avltree, avl_node_t *newnode) {
+	avl_clear_node(newnode);
+	newnode->prev = newnode->next = newnode->parent = NULL;
+	avltree->head = avltree->tail = avltree->top = newnode;
+	return newnode;
+}
+
+avl_node_t *avl_insert_before(avl_tree_t *avltree, avl_node_t *node, avl_node_t *newnode) {
+	if(!node)
+		return avltree->tail
+			? avl_insert_after(avltree, avltree->tail, newnode)
+			: avl_insert_top(avltree, newnode);
+
+	if(node->left)
+		return avl_insert_after(avltree, node->prev, newnode);
+
+	avl_clear_node(newnode);
+
+	newnode->next = node;
+	newnode->parent = node;
+
+	newnode->prev = node->prev;
+	if(node->prev)
+		node->prev->next = newnode;
+	else
+		avltree->head = newnode;
+	node->prev = newnode;
+
+	node->left = newnode;
+	avl_rebalance(avltree, node);
+	return newnode;
+}
+
+avl_node_t *avl_insert_after(avl_tree_t *avltree, avl_node_t *node, avl_node_t *newnode) {
+	if(!node)
+		return avltree->head
+			? avl_insert_before(avltree, avltree->head, newnode)
+			: avl_insert_top(avltree, newnode);
+
+	if(node->right)
+		return avl_insert_before(avltree, node->next, newnode);
+
+	avl_clear_node(newnode);
+
+	newnode->prev = node;
+	newnode->parent = node;
+
+	newnode->next = node->next;
+	if(node->next)
+		node->next->prev = newnode;
+	else
+		avltree->tail = newnode;
+	node->next = newnode;
+
+	node->right = newnode;
+	avl_rebalance(avltree, node);
+	return newnode;
+}
+
+avl_node_t *avl_insert_node(avl_tree_t *avltree, avl_node_t *newnode) {
+	avl_node_t *node;
+
+	if(!avltree->top)
+		return avl_insert_top(avltree, newnode);
+
+	switch(avl_search_closest(avltree, newnode->item, &node)) {
+		case -1:
+			return avl_insert_before(avltree, node, newnode);
+		case 1:
+			return avl_insert_after(avltree, node, newnode);
+	}
+
+	return NULL;
+}
+
+/*
+ * avl_insert:
+ * Create a new node and insert an item there.
+ * Returns the new node on success or NULL if no memory could be allocated.
+ */
+avl_node_t *avl_insert(avl_tree_t *avltree, void *item) {
+	avl_node_t *newnode;
+
+	newnode = avl_init_node(SC_ALLOC(avl_node_t, 1), item);
+	if(newnode) {
+		if(avl_insert_node(avltree, newnode))
+			return newnode;
+		SC_FREE(newnode);
+		/* errno = EEXIST; */
+                return NULL;
+	}
+	SC_ABORT_NOT_REACHED ();
+        return NULL;
+}
+
+/*
+ * avl_unlink_node:
+ * Removes the given node.  Does not delete the item at that node.
+ * The item of the node may be freed before calling avl_unlink_node.
+ * (In other words, it is not referenced by this function.)
+ */
+void avl_unlink_node(avl_tree_t *avltree, avl_node_t *avlnode) {
+	avl_node_t *parent;
+	avl_node_t **superparent;
+	avl_node_t *subst, *left, *right;
+	avl_node_t *balnode;
+
+	if(avlnode->prev)
+		avlnode->prev->next = avlnode->next;
+	else
+		avltree->head = avlnode->next;
+
+	if(avlnode->next)
+		avlnode->next->prev = avlnode->prev;
+	else
+		avltree->tail = avlnode->prev;
+
+	parent = avlnode->parent;
+
+	superparent = parent
+		? avlnode == parent->left ? &parent->left : &parent->right
+		: &avltree->top;
+
+	left = avlnode->left;
+	right = avlnode->right;
+	if(!left) {
+		*superparent = right;
+		if(right)
+			right->parent = parent;
+		balnode = parent;
+	} else if(!right) {
+		*superparent = left;
+		left->parent = parent;
+		balnode = parent;
+	} else {
+		subst = avlnode->prev;
+		if(subst == left) {
+			balnode = subst;
+		} else {
+			balnode = subst->parent;
+			balnode->right = subst->left;
+			if(balnode->right)
+				balnode->right->parent = balnode;
+			subst->left = left;
+			left->parent = subst;
+		}
+		subst->right = right;
+		subst->parent = parent;
+		right->parent = subst;
+		*superparent = subst;
+	}
+
+	avl_rebalance(avltree, balnode);
+}
+
+void *avl_delete_node(avl_tree_t *avltree, avl_node_t *avlnode) {
+	void *item = NULL;
+	if(avlnode) {
+		item = avlnode->item;
+		avl_unlink_node(avltree, avlnode);
+		if(avltree->freeitem)
+			avltree->freeitem(item);
+		SC_FREE(avlnode);
+	}
+	return item;
+}
+
+void *avl_delete(avl_tree_t *avltree, const void *item) {
+	return avl_delete_node(avltree, avl_search(avltree, item));
+}
+
+avl_node_t *avl_fixup_node(avl_tree_t *avltree, avl_node_t *newnode) {
+	avl_node_t *oldnode = NULL, *node;
+
+	if(!avltree || !newnode)
+		return NULL;
+
+	node = newnode->prev;
+	if(node) {
+		oldnode = node->next;
+		node->next = newnode;
+	} else {
+		avltree->head = newnode;
+	}
+
+	node = newnode->next;
+	if(node) {
+		oldnode = node->prev;
+		node->prev = newnode;
+	} else {
+		avltree->tail = newnode;
+	}
+
+	node = newnode->parent;
+	if(node) {
+		if(node->left == oldnode)
+			node->left = newnode;
+		else
+			node->right = newnode;
+	} else {
+		oldnode = avltree->top;
+		avltree->top = newnode;
+	}
+
+	return oldnode;
+}
+
+/*
+ * avl_rebalance:
+ * Rebalances the tree if one side becomes too heavy.  This function
+ * assumes that both subtrees are AVL-trees with consistant data.  The
+ * function has the additional side effect of recalculating the count of
+ * the tree at this node.  It should be noted that at the return of this
+ * function, if a rebalance takes place, the top of this subtree is no
+ * longer going to be the same node.
+ */
+void avl_rebalance(avl_tree_t *avltree, avl_node_t *avlnode) {
+	avl_node_t *child;
+	avl_node_t *gchild;
+	avl_node_t *parent;
+	avl_node_t **superparent;
+
+	parent = avlnode;
+
+	while(avlnode) {
+		parent = avlnode->parent;
+
+		superparent = parent
+			? avlnode == parent->left ? &parent->left : &parent->right
+			: &avltree->top;
+
+		switch(avl_check_balance(avlnode)) {
+		case -1:
+			child = avlnode->left;
+			#ifdef AVL_DEPTH
+			if(L_DEPTH(child) >= R_DEPTH(child)) {
+			#else
+			#ifdef AVL_COUNT
+			if(L_COUNT(child) >= R_COUNT(child)) {
+			#else
+			#error No balancing possible.
+			#endif
+			#endif
+				avlnode->left = child->right;
+				if(avlnode->left)
+					avlnode->left->parent = avlnode;
+				child->right = avlnode;
+				avlnode->parent = child;
+				*superparent = child;
+				child->parent = parent;
+				#ifdef AVL_COUNT
+				avlnode->count = CALC_COUNT(avlnode);
+				child->count = CALC_COUNT(child);
+				#endif
+				#ifdef AVL_DEPTH
+				avlnode->depth = CALC_DEPTH(avlnode);
+				child->depth = CALC_DEPTH(child);
+				#endif
+			} else {
+				gchild = child->right;
+				avlnode->left = gchild->right;
+				if(avlnode->left)
+					avlnode->left->parent = avlnode;
+				child->right = gchild->left;
+				if(child->right)
+					child->right->parent = child;
+				gchild->right = avlnode;
+				if(gchild->right)
+					gchild->right->parent = gchild;
+				gchild->left = child;
+				if(gchild->left)
+					gchild->left->parent = gchild;
+				*superparent = gchild;
+				gchild->parent = parent;
+				#ifdef AVL_COUNT
+				avlnode->count = CALC_COUNT(avlnode);
+				child->count = CALC_COUNT(child);
+				gchild->count = CALC_COUNT(gchild);
+				#endif
+				#ifdef AVL_DEPTH
+				avlnode->depth = CALC_DEPTH(avlnode);
+				child->depth = CALC_DEPTH(child);
+				gchild->depth = CALC_DEPTH(gchild);
+				#endif
+			}
+		break;
+		case 1:
+			child = avlnode->right;
+			#ifdef AVL_DEPTH
+			if(R_DEPTH(child) >= L_DEPTH(child)) {
+			#else
+			#ifdef AVL_COUNT
+			if(R_COUNT(child) >= L_COUNT(child)) {
+			#else
+			#error No balancing possible.
+			#endif
+			#endif
+				avlnode->right = child->left;
+				if(avlnode->right)
+					avlnode->right->parent = avlnode;
+				child->left = avlnode;
+				avlnode->parent = child;
+				*superparent = child;
+				child->parent = parent;
+				#ifdef AVL_COUNT
+				avlnode->count = CALC_COUNT(avlnode);
+				child->count = CALC_COUNT(child);
+				#endif
+				#ifdef AVL_DEPTH
+				avlnode->depth = CALC_DEPTH(avlnode);
+				child->depth = CALC_DEPTH(child);
+				#endif
+			} else {
+				gchild = child->left;
+				avlnode->right = gchild->left;
+				if(avlnode->right)
+					avlnode->right->parent = avlnode;
+				child->left = gchild->right;
+				if(child->left)
+					child->left->parent = child;
+				gchild->left = avlnode;
+				if(gchild->left)
+					gchild->left->parent = gchild;
+				gchild->right = child;
+				if(gchild->right)
+					gchild->right->parent = gchild;
+				*superparent = gchild;
+				gchild->parent = parent;
+				#ifdef AVL_COUNT
+				avlnode->count = CALC_COUNT(avlnode);
+				child->count = CALC_COUNT(child);
+				gchild->count = CALC_COUNT(gchild);
+				#endif
+				#ifdef AVL_DEPTH
+				avlnode->depth = CALC_DEPTH(avlnode);
+				child->depth = CALC_DEPTH(child);
+				gchild->depth = CALC_DEPTH(gchild);
+				#endif
+			}
+		break;
+		default:
+			#ifdef AVL_COUNT
+			avlnode->count = CALC_COUNT(avlnode);
+			#endif
+			#ifdef AVL_DEPTH
+			avlnode->depth = CALC_DEPTH(avlnode);
+			#endif
+		}
+		avlnode = parent;
+	}
+}
+
+/* CB ugly fix for wrong indent level */
+#if(0)
+        }}
+#endif
+
+/* *INDENT-ON* */
+
+typedef struct avl_to_array_data
+{
+  size_t              iz;
+  sc_array_t         *array;
+}
+avl_to_array_data_t;
+
+typedef struct avl_foreach_recursion_data
+{
+  avl_foreach_t       callback;
+  void               *data;
+}
+avl_foreach_recursion_data_t;
+
+static void
+avl_to_array_foreach (void *item, void *data)
+{
+  void              **pp;
+  avl_to_array_data_t *adata = (avl_to_array_data_t *) data;
+
+  pp = (void **) sc_array_index (adata->array, adata->iz);
+  *pp = item;
+
+  ++adata->iz;
+}
+
+static void
+avl_foreach_recursion (avl_node_t * node, avl_foreach_recursion_data_t * rec)
+{
+  if (node->left != NULL)
+    avl_foreach_recursion (node->left, rec);
+
+  rec->callback (node->item, rec->data);
+
+  if (node->right != NULL)
+    avl_foreach_recursion (node->right, rec);
+}
+
+void
+avl_foreach (avl_tree_t * avltree, avl_foreach_t callback, void *data)
+{
+  avl_foreach_recursion_data_t rec;
+
+  rec.callback = callback;
+  rec.data = data;
+
+  if (avltree->top != NULL)
+    avl_foreach_recursion (avltree->top, &rec);
+}
+
+#ifdef AVL_COUNT
+
+void
+avl_to_array (avl_tree_t * avltree, sc_array_t * array)
+{
+  avl_to_array_data_t adata;
+
+  SC_ASSERT (array->elem_size == sizeof (void *));
+
+  sc_array_resize (array, avl_count (avltree));
+
+  adata.iz = 0;
+  adata.array = array;
+  avl_foreach (avltree, avl_to_array_foreach, &adata);
+  SC_ASSERT (adata.iz == adata.array->elem_count);
+}
+
+#endif /* AVL_COUNT */
diff --git a/sc/src/sc_avl.h b/sc/src/sc_avl.h
new file mode 100644
index 0000000..4e0cf05
--- /dev/null
+++ b/sc/src/sc_avl.h
@@ -0,0 +1,212 @@
+/* *INDENT-OFF* */
+
+/*****************************************************************************
+
+    avl.h - Source code for the AVL-tree library.
+
+    Copyright (C) 1998  Michael H. Buselli <cosine at cosine.org>
+    Copyright (C) 2000-2002  Wessel Dankers <wsl at nl.linux.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+
+    Augmented AVL-tree. Original by Michael H. Buselli <cosine at cosine.org>.
+
+    Modified by Wessel Dankers <wsl at nl.linux.org> to add a bunch of bloat to
+    the sourcecode, change the interface and squash a few bugs.
+    Mail him if you find new bugs.
+
+*****************************************************************************/
+
+/* renamed from avl.h to sc_avl.h for the SC Library and modified */
+
+#ifndef _AVL_H
+#define _AVL_H
+
+#include <sc_containers.h>
+
+SC_EXTERN_C_BEGIN;
+
+/* modification to use count only */
+#define AVL_COUNT
+
+/* We need either depths, counts or both (the latter being the default) */
+#if !defined(AVL_DEPTH) && !defined(AVL_COUNT)
+#define AVL_DEPTH
+#define AVL_COUNT
+#endif
+
+/* User supplied function to compare two items like strcmp() does.
+ * For example: cmp(a,b) will return:
+ *   -1  if a < b
+ *    0  if a = b
+ *    1  if a > b
+ */
+typedef int (*avl_compare_t)(const void *, const void *);
+
+/* User supplied function to delete an item when a node is free()d.
+ * If NULL, the item is not free()d.
+ */
+typedef void (*avl_freeitem_t)(void *);
+
+/* User supplied function to call on an item in foreach().
+ */
+typedef void (*avl_foreach_t)(void *, void *);
+
+typedef struct avl_node_t {
+	struct avl_node_t *next;
+	struct avl_node_t *prev;
+	struct avl_node_t *parent;
+	struct avl_node_t *left;
+	struct avl_node_t *right;
+	void *item;
+#ifdef AVL_COUNT
+	unsigned int count;
+#endif
+#ifdef AVL_DEPTH
+	unsigned char depth;
+#endif
+} avl_node_t;
+
+typedef struct avl_tree_t {
+	avl_node_t *head;
+	avl_node_t *tail;
+	avl_node_t *top;
+	avl_compare_t cmp;
+	avl_freeitem_t freeitem;
+} avl_tree_t;
+
+/* Initializes a new tree for elements that will be ordered using
+ * the supplied strcmp()-like function.
+ * Returns the value of avltree (even if it's NULL).
+ * O(1) */
+extern avl_tree_t *avl_init_tree(avl_tree_t *avltree, avl_compare_t, avl_freeitem_t);
+
+/* Allocates and initializes a new tree for elements that will be
+ * ordered using the supplied strcmp()-like function.
+ * Aborts if memory could not be allocated.
+ * O(1) */
+extern avl_tree_t *avl_alloc_tree(avl_compare_t, avl_freeitem_t);
+
+/* Frees the entire tree efficiently. Nodes will be free()d.
+ * If the tree's freeitem is not NULL it will be invoked on every item.
+ * O(n) */
+extern void avl_free_tree(avl_tree_t *);
+
+/* Reinitializes the tree structure for reuse. Nothing is free()d.
+ * Compare and freeitem functions are left alone.
+ * O(1) */
+extern void avl_clear_tree(avl_tree_t *);
+
+/* Free()s all nodes in the tree but leaves the tree itself.
+ * If the tree's freeitem is not NULL it will be invoked on every item.
+ * O(n) */
+extern void avl_free_nodes(avl_tree_t *);
+
+/* Initializes memory for use as a node. Returns NULL if avlnode is NULL.
+ * O(1) */
+extern avl_node_t *avl_init_node(avl_node_t *avlnode, void *item);
+
+/* Insert an item into the tree and return the new node.
+ * Aborts if memory for the new node could not be allocated.
+ * Returns NULL if the node is already in the tree, or the new node.
+ * O(lg n) */
+extern avl_node_t *avl_insert(avl_tree_t *, void *item);
+
+/* Insert a node into the tree and return it.
+ * Returns NULL if the node is already in the tree.
+ * O(lg n) */
+extern avl_node_t *avl_insert_node(avl_tree_t *, avl_node_t *);
+
+/* Insert a node in an empty tree. If avlnode is NULL, the tree will be
+ * cleared and ready for re-use.
+ * If the tree is not empty, the old nodes are left dangling.
+ * O(1) */
+extern avl_node_t *avl_insert_top(avl_tree_t *, avl_node_t *avlnode);
+
+/* Insert a node before another node. Returns the new node.
+ * If old is NULL, the item is appended to the tree.
+ * O(lg n) */
+extern avl_node_t *avl_insert_before(avl_tree_t *, avl_node_t *old, avl_node_t *nw);
+
+/* Insert a node after another node. Returns the new node.
+ * If old is NULL, the item is prepended to the tree.
+ * O(lg n) */
+extern avl_node_t *avl_insert_after(avl_tree_t *, avl_node_t *old, avl_node_t *nw);
+
+/* Deletes a node from the tree. Returns immediately if the node is NULL.
+ * The item will not be free()d regardless of the tree's freeitem handler.
+ * This function comes in handy if you need to update the search key.
+ * O(lg n) */
+extern void avl_unlink_node(avl_tree_t *, avl_node_t *);
+
+/* Deletes a node from the tree. Returns immediately if the node is NULL.
+ * If the tree's freeitem is not NULL, it is invoked on the item.
+ * If it is, returns the item.
+ * O(lg n) */
+extern void *avl_delete_node(avl_tree_t *, avl_node_t *);
+
+/* Searches for an item in the tree and deletes it if found.
+ * If the tree's freeitem is not NULL, it is invoked on the item.
+ * If it is, returns the item.
+ * O(lg n) */
+extern void *avl_delete(avl_tree_t *, const void *item);
+
+/* If exactly one node is moved in memory, this will fix the pointers
+ * in the tree that refer to it. It must be an exact shallow copy.
+ * Returns the pointer to the old position.
+ * O(1) */
+extern avl_node_t *avl_fixup_node(avl_tree_t *, avl_node_t *nw);
+
+/* Searches for a node with the key closest (or equal) to the given item.
+ * If avlnode is not NULL, *avlnode will be set to the node found or NULL
+ * if the tree is empty. Return values:
+ *   -1  if the returned node is smaller
+ *    0  if the returned node is equal or if the tree is empty
+ *    1  if the returned node is greater
+ * O(lg n) */
+extern int avl_search_closest(const avl_tree_t *, const void *item, avl_node_t **avlnode);
+
+/* Searches for the item in the tree and returns a matching node if found
+ * or NULL if not.
+ * O(lg n) */
+extern avl_node_t *avl_search(const avl_tree_t *, const void *item);
+
+/* Calls the callback function for every item in the tree in order.
+ * O(n) */
+extern void avl_foreach(avl_tree_t *, avl_foreach_t, void *);
+
+#ifdef AVL_COUNT
+/* Returns the number of nodes in the tree.
+ * O(1) */
+extern unsigned int avl_count(const avl_tree_t *);
+
+/* Searches a node by its rank in the list. Counting starts at 0.
+ * Returns NULL if the index exceeds the number of nodes in the tree.
+ * O(lg n) */
+extern avl_node_t *avl_at(const avl_tree_t *, unsigned int);
+
+/* Returns the rank of a node in the list. Counting starts at 0.
+ * O(lg n) */
+extern unsigned int avl_index(const avl_node_t *);
+
+/* Copies all items into an array of void *.
+* O(n) */
+extern void avl_to_array (avl_tree_t *, sc_array_t *);
+
+#endif /* AVL_COUNT */
+
+SC_EXTERN_C_END;
+
+#endif /* !_AVL_H */
diff --git a/sc/src/sc_blas.c b/sc/src/sc_blas.c
new file mode 100644
index 0000000..d42820a
--- /dev/null
+++ b/sc/src/sc_blas.c
@@ -0,0 +1,40 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_blas.h>
+
+const char          sc_transchar[] = { 'N', 'T', 'C' };
+const char          sc_antitranschar[] = { 'T', 'N', '?' };
+const char          sc_uplochar[] = { 'U', 'L', '?' };
+const char          sc_cmachchar[] =
+  { 'E', 'S', 'B', 'P', 'N', 'R', 'M', 'U', 'L', 'O' };
+
+#ifndef SC_WITH_BLAS
+
+int
+sc_blas_nonimplemented ()
+{
+  SC_ABORT ("BLAS not compiled in this configuration");
+  return 0;
+}
+
+#endif /* !SC_WITH_BLAS */
diff --git a/sc/src/sc_blas.h b/sc/src/sc_blas.h
new file mode 100644
index 0000000..1bb3aa9
--- /dev/null
+++ b/sc/src/sc_blas.h
@@ -0,0 +1,134 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_BLAS_H
+#define SC_BLAS_H
+
+#include <sc.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef int         sc_bint_t;  /* Integer type for all of the blas calls */
+typedef unsigned int sc_buint_t;        /* Unsigned Integer type for blas */
+
+typedef enum sc_trans
+{
+  SC_NO_TRANS,
+  SC_TRANS,
+  SC_TRANS_ANCHOR
+}
+sc_trans_t;
+
+typedef enum sc_uplo
+{
+  SC_UPPER,
+  SC_LOWER,
+  SC_UPLO_ANCHOR
+}
+sc_uplo_t;
+
+typedef enum sc_cmach
+{
+  SC_CMACH_EPS,                 /* relative machine precision */
+  SC_CMACH_SFMIN,               /* safe minimum, such that 1/sfmin does not
+                                 * overflow
+                                 */
+  SC_CMACH_BASE,                /* base of the machine */
+  SC_CMACH_PREC,                /* eps*base */
+  SC_CMACH_T,                   /* number of (base) digits in the mantissa */
+  SC_CMACH_RND,                 /* 1.0 when rounding occurs in addition,
+                                 * 0.0 otherwise
+                                 */
+  SC_CMACH_EMIN,                /* minimum exponent before (gradual)
+                                 * underflow
+                                 */
+  SC_CMACH_RMIN,                /* underflow threshold - base**(emin-1) */
+  SC_CMACH_EMAX,                /* largest exponent before overflow */
+  SC_CMACH_RMAX,                /* overflow threshold  - (base**emax)*(1-eps) */
+  SC_CMACH_ANCHOR
+}
+sc_cmach_t;
+
+extern const char   sc_transchar[];
+extern const char   sc_antitranschar[]; /* does not work for complex */
+extern const char   sc_uplochar[];
+extern const char   sc_cmachchar[];
+
+#ifdef SC_WITH_BLAS
+
+#ifndef SC_F77_FUNC
+#define SC_F77_FUNC(small,CAPS) small ## _
+#endif
+
+#define SC_BLAS_DLAMCH  SC_F77_FUNC(dlamch,DLAMCH)
+#define SC_BLAS_DSCAL   SC_F77_FUNC(dscal,DSCAL)
+#define SC_BLAS_DCOPY   SC_F77_FUNC(dcopy,DCOPY)
+#define SC_BLAS_DAXPY   SC_F77_FUNC(daxpy,DAXPY)
+#define SC_BLAS_DDOT    SC_F77_FUNC(ddot,DDOT)
+#define SC_BLAS_DGEMV   SC_F77_FUNC(dgemv,DGEMV)
+#define SC_BLAS_DGEMM   SC_F77_FUNC(dgemm,DGEMM)
+
+double              SC_BLAS_DLAMCH (const char *cmach);
+void                SC_BLAS_DSCAL (const sc_bint_t * n, const double *alpha,
+                                   double *X, const sc_bint_t * incx);
+void                SC_BLAS_DCOPY (const sc_bint_t * n,
+                                   const double *X, const sc_bint_t * incx,
+                                   double *Y, const sc_bint_t * incy);
+void                SC_BLAS_DAXPY (const sc_bint_t * n, const double *alpha,
+                                   const double *X, const sc_bint_t * incx,
+                                   double *Y, const sc_bint_t * incy);
+double              SC_BLAS_DDOT (const sc_bint_t * n, const double *X,
+                                  const sc_bint_t * incx, const double *Y,
+                                  const sc_bint_t * incy);
+
+void                SC_BLAS_DGEMV (const char *transa, const sc_bint_t * m,
+                                   const sc_bint_t * n, const double *alpha,
+                                   const double *a, const sc_bint_t * lda,
+                                   const double *x, const sc_bint_t * incx,
+                                   const double *beta, double *y,
+                                   const sc_bint_t * incy);
+
+void                SC_BLAS_DGEMM (const char *transa, const char *transb,
+                                   const sc_bint_t * m, const sc_bint_t * n,
+                                   const sc_bint_t * k, const double *alpha,
+                                   const double *a, const sc_bint_t * lda,
+                                   const double *b, const sc_bint_t * ldb,
+                                   const double *beta, double *c,
+                                   const sc_bint_t * ldc);
+
+#else /* !SC_WITH_BLAS */
+
+#define SC_BLAS_DLAMCH (double) sc_blas_nonimplemented
+#define SC_BLAS_DSCAL  (void)   sc_blas_nonimplemented
+#define SC_BLAS_DCOPY  (void)   sc_blas_nonimplemented
+#define SC_BLAS_DAXPY  (void)   sc_blas_nonimplemented
+#define SC_BLAS_DDOT   (double) sc_blas_nonimplemented
+#define SC_BLAS_DGEMM  (void)   sc_blas_nonimplemented
+#define SC_BLAS_DGEMV  (void)   sc_blas_nonimplemented
+
+int                 sc_blas_nonimplemented ();
+
+#endif
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_BLAS_H */
diff --git a/sc/src/sc_bspline.c b/sc/src/sc_bspline.c
new file mode 100644
index 0000000..c4dea6c
--- /dev/null
+++ b/sc/src/sc_bspline.c
@@ -0,0 +1,509 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_bspline.h>
+
+int
+sc_bspline_min_number_points (int n)
+{
+  return n + 1;
+}
+
+int
+sc_bspline_min_number_knots (int n)
+{
+  return 2 * n + 2;
+}
+
+sc_dmatrix_t       *
+sc_bspline_knots_new (int n, sc_dmatrix_t * points)
+{
+#ifdef SC_DEBUG
+  const int           d = points->n;
+#endif
+  const int           p = points->m - 1;
+  const int           m = n + p + 1;
+  const int           l = m - 2 * n;
+  int                 i;
+  sc_dmatrix_t       *knots;
+  double             *knotse;
+
+  SC_ASSERT (n >= 0 && m >= 1 && d >= 1 && l >= 1);
+
+  knots = sc_dmatrix_new (m + 1, 1);
+  knotse = knots->e[0];
+
+  for (i = 0; i < n; ++i) {
+    knotse[i] = 0.;
+    knotse[m - i] = 1.;
+  }
+  for (i = 0; i <= l; ++i) {
+    knotse[n + i] = i / (double) l;
+  }
+
+  return knots;
+}
+
+sc_dmatrix_t       *
+sc_bspline_knots_new_length (int n, sc_dmatrix_t * points)
+{
+  const int           d = points->n;
+  const int           p = points->m - 1;
+  const int           m = n + p + 1;
+  const int           l = m - 2 * n;
+  int                 i, k;
+  double              distsqr, distsum, distalln;
+  double             *knotse;
+  sc_dmatrix_t       *knots;
+
+  SC_ASSERT (n >= 1);
+  SC_ASSERT (n >= 0 && m >= 1 && d >= 1 && l >= 1);
+
+  knots = sc_dmatrix_new_zero (m + 1, 1);
+  knotse = knots->e[0];
+
+  /* compute cumulative distance from P_0 and hide inside knots */
+  distsum = 0.;
+  for (i = 0; i < p; ++i) {
+    SC_ASSERT (n + i + 2 >= 0 && n + i + 2 <= m);
+    distsqr = 0.;
+    for (k = 0; k < d; ++k) {
+      distsqr += SC_SQR (points->e[i + 1][k] - points->e[i][k]);
+    }
+    knotse[n + i + 2] = distsum += sqrt (distsqr);
+  }
+  distalln = distsum * n;
+
+  /* assign average cumulative distance to knot value */
+  for (i = 1; i < l; ++i) {
+    distsum = 0.;
+    for (k = 0; k < n; ++k) {
+      SC_ASSERT (n + i + k + 1 <= m);
+      distsum += knotse[n + i + k + 1];
+    }
+    knotse[n + i] = distsum / distalln;
+  }
+
+  /* fill in the beginning and end values */
+  for (i = 0; i <= n; ++i) {
+    knotse[i] = 0.;
+    knotse[m - i] = 1.;
+  }
+
+  return knots;
+}
+
+sc_dmatrix_t       *
+sc_bspline_knots_new_periodic (int n, sc_dmatrix_t * points)
+{
+#ifdef SC_DEBUG
+  const int           d = points->n;
+#endif
+  const int           p = points->m - 1;
+  const int           m = n + p + 1;
+  const int           l = m - 2 * n;
+  int                 i;
+  sc_dmatrix_t       *knots;
+  double             *knotse;
+
+  SC_ASSERT (n >= 0 && m >= 1 && d >= 1 && l >= 1);
+
+  knots = sc_dmatrix_new (m + 1, 1);
+  knotse = knots->e[0];
+
+  for (i = 0; i <= m; ++i) {
+    knotse[i] = (i - n) / (double) l;
+  }
+
+  return knots;
+}
+
+sc_dmatrix_t       *
+sc_bspline_knots_new_length_periodic (int n, sc_dmatrix_t * points)
+{
+  const int           d = points->n;
+  const int           p = points->m - 1;
+  const int           m = n + p + 1;
+  const int           l = m - 2 * n;
+  int                 i, k;
+  double              distsqr, distsum, distalln;
+  double             *knotse;
+  sc_dmatrix_t       *knots;
+
+  SC_ASSERT (n >= 1);
+  SC_ASSERT (n >= 0 && m >= 1 && d >= 1 && l >= 1);
+
+  knots = sc_dmatrix_new_zero (m + 1, 1);
+  knotse = knots->e[0];
+
+  /* compute cumulative distance from P_0 and hide inside knots */
+  distsum = 0.;
+  for (i = 0; i < p; ++i) {
+    SC_ASSERT (n + i + 2 >= 0 && n + i + 2 <= m);
+    distsqr = 0.;
+    for (k = 0; k < d; ++k) {
+      distsqr += SC_SQR (points->e[i + 1][k] - points->e[i][k]);
+    }
+    knotse[n + i + 2] = sqrt (distsqr);
+    if (i < l) {
+      distsum += knotse[n + i + 2];
+    }
+  }
+  distalln = distsum * n;
+
+  /* assign average cumulative distance to knot value */
+  knotse[n] = 0.;
+  for (i = 1; i < l; ++i) {
+    distsum = 0.;
+    for (k = 0; k < n; ++k) {
+      SC_ASSERT (n + i + k + 1 <= m);
+      distsum += knotse[n + i + k + 1];
+    }
+    knotse[n + i] = knotse[n + i - 1] + distsum / distalln;
+  }
+  knotse[n + l] = 1.;
+
+  /* fill in the beginning and end values */
+  for (i = 0; i < n; ++i) {
+    knotse[i] = knotse[i + l] - 1.;
+    knotse[m - i] = knotse[2 * n - i] + 1.;
+  }
+
+  return knots;
+}
+
+void
+sc_bspline_make_points_periodic (int n, sc_dmatrix_t * points)
+{
+  const int           d = points->n;
+  const int           l = points->m;
+  const int           p = n + l - 1;
+  const int           shift = (n / 2);
+  int                 i, j;
+
+  SC_ASSERT (d > 0 && n >= 0 && l > 0);
+
+  if (!n) {
+    /* already periodic */
+    return;
+  }
+
+  sc_dmatrix_resize (points, p + 1, d);
+
+  /* shift to make room for the starting points */
+  for (i = l - 1; i >= 0; i--) {
+    for (j = 0; j < d; j++) {
+      SC_ASSERT (i + shift <= p);
+      points->e[i + shift][j] = points->e[i][j];
+    }
+  }
+
+  /* copy the starting points */
+  for (i = 0; i < shift; i++) {
+    for (j = 0; j < d; j++) {
+      SC_ASSERT (i + l <= p);
+      points->e[i][j] = points->e[i + l][j];
+    }
+  }
+
+  /* copy the ending points */
+  for (i = shift + l; i <= p; i++) {
+    for (j = 0; j < d; j++) {
+      SC_ASSERT (i - l >= 0);
+      points->e[i][j] = points->e[i - l][j];
+    }
+  }
+
+  SC_ASSERT (sc_dmatrix_is_valid (points));
+}
+
+sc_dmatrix_t       *
+sc_bspline_workspace_new (int n, int d)
+{
+  SC_ASSERT (n >= 0 && d >= 1);
+
+  return sc_dmatrix_new ((n + 1) * (n + 1), d);
+}
+
+sc_bspline_t       *
+sc_bspline_new (int n, sc_dmatrix_t * points,
+                sc_dmatrix_t * knots, sc_dmatrix_t * works)
+{
+  sc_bspline_t       *bs;
+
+  bs = SC_ALLOC_ZERO (sc_bspline_t, 1);
+  bs->d = points->n;
+  bs->p = points->m - 1;
+  bs->n = n;
+  bs->m = bs->n + bs->p + 1;
+  bs->l = bs->m - 2 * bs->n;
+  bs->cacheknot = n;
+
+  SC_ASSERT (n >= 0 && bs->m >= 1 && bs->d >= 1 && bs->l >= 1);
+
+  bs->points = points;
+  if (knots == NULL) {
+    bs->knots = sc_bspline_knots_new (bs->n, points);
+    bs->knots_owned = 1;
+  }
+  else {
+    SC_ASSERT (knots->m == bs->m + 1);
+    SC_ASSERT (knots->n == 1);
+    bs->knots = knots;
+    bs->knots_owned = 0;
+  }
+  if (works == NULL) {
+    bs->works = sc_bspline_workspace_new (bs->n, bs->d);
+    bs->works_owned = 1;
+  }
+  else {
+    SC_ASSERT (works->m == (bs->n + 1) * (bs->n + 1));
+    SC_ASSERT (works->n == bs->d);
+    bs->works = works;
+    bs->works_owned = 0;
+  }
+
+  return bs;
+}
+
+void
+sc_bspline_destroy (sc_bspline_t * bs)
+{
+  if (bs->knots_owned)
+    sc_dmatrix_destroy (bs->knots);
+  if (bs->works_owned)
+    sc_dmatrix_destroy (bs->works);
+
+  SC_FREE (bs);
+}
+
+static int
+sc_bspline_find_interval (sc_bspline_t * bs, double t)
+{
+  int                 i, iguess;
+  double              t0, t1;
+  const double       *knotse = bs->knots->e[0];
+
+  t0 = knotse[bs->n];
+  t1 = knotse[bs->n + bs->l];
+  SC_ASSERT (t >= t0 && t <= t1);
+  SC_ASSERT (bs->cacheknot >= bs->n && bs->cacheknot < bs->n + bs->l);
+
+  if (t >= t1) {
+    iguess = bs->cacheknot = bs->n + bs->l - 1;
+  }
+  else if (knotse[bs->cacheknot] <= t && t < knotse[bs->cacheknot + 1]) {
+    iguess = bs->cacheknot;
+  }
+  else {
+    const int           nshift = 1;
+    int                 ileft, iright;
+    double              tleft, tright;
+
+    ileft = bs->n;
+    iright = bs->n + bs->l - 1;
+    iguess = bs->n + (int) floor ((t - t0) / (t1 - t0) * bs->l);
+    iguess = SC_MAX (iguess, ileft);
+    iguess = SC_MIN (iguess, iright);
+
+    for (i = 0;; ++i) {
+      tleft = knotse[iguess];
+      tright = knotse[iguess + 1];
+      if (t < tleft) {
+        iright = iguess - 1;
+        if (i < nshift) {
+          iguess = iright;
+        }
+        else {
+          iguess = (ileft + iright + 1) / 2;
+        }
+      }
+      else if (t >= tright) {
+        ileft = iguess + 1;
+        if (i < nshift) {
+          iguess = ileft;
+        }
+        else {
+          iguess = (ileft + iright) / 2;
+        }
+      }
+      else {
+        if (i > 0) {
+          SC_LDEBUGF ("For %g needed %d search steps\n", t, i);
+        }
+        break;
+      }
+    }
+    bs->cacheknot = iguess;
+  }
+  SC_ASSERT (iguess >= bs->n && iguess < bs->n + bs->l);
+  SC_CHECK_ABORT ((knotse[iguess] <= t && t < knotse[iguess + 1]) ||
+                  (t >= t1 && iguess == bs->n + bs->l - 1),
+                  "Bug in sc_bspline_find_interval");
+
+  return iguess;
+}
+
+void
+sc_bspline_evaluate (sc_bspline_t * bs, double t, double *result)
+{
+  int                 i, k, n;
+  int                 iguess;
+  int                 toffset;
+  double             *wfrom, *wto;
+  const double       *knotse = bs->knots->e[0];
+
+  iguess = sc_bspline_find_interval (bs, t);
+
+  toffset = 0;
+  wfrom = wto = bs->points->e[iguess - bs->n];
+  for (n = bs->n; n > 0; --n) {
+    wto = bs->works->e[toffset];
+
+    for (i = 0; i < n; ++i) {
+      const double        tleft = knotse[iguess + i - n + 1];
+      const double        tright = knotse[iguess + i + 1];
+      const double        tfactor = 1. / (tright - tleft);
+
+      for (k = 0; k < bs->d; ++k) {
+        wto[bs->d * i + k] =
+          ((t - tleft) * wfrom[bs->d * (i + 1) + k] +
+           (tright - t) * wfrom[bs->d * i + k]) * tfactor;
+      }
+    }
+
+    wfrom = wto;
+    toffset += n;
+  }
+  SC_ASSERT (toffset == bs->n * (bs->n + 1) / 2);
+
+  memcpy (result, wfrom, sizeof (double) * bs->d);
+}
+
+void
+sc_bspline_derivative (sc_bspline_t * bs, double t, double *result)
+{
+  sc_bspline_derivative_n (bs, 1, t, result);
+}
+
+void
+sc_bspline_derivative_n (sc_bspline_t * bs, int order,
+                         double t, double *result)
+{
+  int                 i, k, n;
+  int                 iguess;
+  int                 toffset;
+  double             *wfrom, *wto;
+  const double       *knotse = bs->knots->e[0];
+
+  SC_ASSERT (order >= 0);
+
+  if (bs->n < order) {
+    memset (result, 0, sizeof (double) * bs->d);
+    return;
+  }
+
+  iguess = sc_bspline_find_interval (bs, t);
+
+  toffset = 0;
+  wfrom = wto = bs->points->e[iguess - bs->n];
+  for (n = bs->n; n > 0; --n) {
+    wto = bs->works->e[toffset];
+
+    if (bs->n < n + order) {
+      for (i = 0; i < n; ++i) {
+        const double        tleft = knotse[iguess + i - n + 1];
+        const double        tright = knotse[iguess + i + 1];
+        const double        tfactor = n / (tright - tleft);
+
+        for (k = 0; k < bs->d; ++k) {
+          wto[bs->d * i + k] =
+            (wfrom[bs->d * (i + 1) + k] - wfrom[bs->d * i + k]) * tfactor;
+        }
+      }
+    }
+    else {
+      for (i = 0; i < n; ++i) {
+        const double        tleft = knotse[iguess + i - n + 1];
+        const double        tright = knotse[iguess + i + 1];
+        const double        tfactor = 1. / (tright - tleft);
+
+        for (k = 0; k < bs->d; ++k) {
+          wto[bs->d * i + k] =
+            ((t - tleft) * wfrom[bs->d * (i + 1) + k] +
+             (tright - t) * wfrom[bs->d * i + k]) * tfactor;
+        }
+      }
+    }
+
+    wfrom = wto;
+    toffset += n;
+  }
+  SC_ASSERT (toffset == bs->n * (bs->n + 1) / 2);
+
+  memcpy (result, wfrom, sizeof (double) * bs->d);
+}
+
+void
+sc_bspline_derivative2 (sc_bspline_t * bs, double t, double *result)
+{
+  int                 i, k, n;
+  int                 iguess;
+  int                 toffset;
+  double             *pfrom, *pto;
+  double             *qfrom, *qto;
+  const double       *knotse = bs->knots->e[0];
+
+  iguess = sc_bspline_find_interval (bs, t);
+
+  toffset = bs->n + 1;
+  pfrom = pto = bs->works->e[0];
+  qfrom = qto = bs->points->e[iguess - bs->n];
+  memset (pfrom, 0, toffset * bs->d * sizeof (double));
+  for (n = bs->n; n > 0; --n) {
+    pto = bs->works->e[toffset];
+    qto = bs->works->e[toffset + n];
+
+    for (i = 0; i < n; ++i) {
+      const double        tleft = knotse[iguess + i - n + 1];
+      const double        tright = knotse[iguess + i + 1];
+      const double        tfactor = 1. / (tright - tleft);
+
+      for (k = 0; k < bs->d; ++k) {
+        pto[bs->d * i + k] =
+          ((t - tleft) * pfrom[bs->d * (i + 1) + k] +
+           (tright - t) * pfrom[bs->d * i + k] +
+           qfrom[bs->d * (i + 1) + k] - qfrom[bs->d * i + k]) * tfactor;
+        qto[bs->d * i + k] =
+          ((t - tleft) * qfrom[bs->d * (i + 1) + k] +
+           (tright - t) * qfrom[bs->d * i + k]) * tfactor;
+      }
+    }
+
+    pfrom = pto;
+    qfrom = qto;
+    toffset += 2 * n;
+  }
+  SC_ASSERT (toffset == (bs->n + 1) * (bs->n + 1));
+
+  memcpy (result, pfrom, sizeof (double) * bs->d);
+}
diff --git a/sc/src/sc_bspline.h b/sc/src/sc_bspline.h
new file mode 100644
index 0000000..eb8a976
--- /dev/null
+++ b/sc/src/sc_bspline.h
@@ -0,0 +1,167 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_BSPLINE_H
+#define SC_BSPLINE_H
+
+#include <sc_dmatrix.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef struct
+{
+  int                 d; /** Dimensionality of control points */
+  int                 p; /** Number of control points is p + 1 */
+  int                 n; /** Polynomial degree is n >= 0 */
+  int                 m; /** Number of knots is m + 1 =  n + p + 2 */
+  int                 l; /** Number of internal intervals l = m - 2 * n > 0 */
+  int                 cacheknot;        /* previously evaluated knot interval */
+  sc_dmatrix_t       *points;   /* (p + 1) x d array of points, not owned */
+  sc_dmatrix_t       *knots;    /* m + 1 array of knots */
+  int                 knots_owned;
+  sc_dmatrix_t       *works;    /* Workspace ((n + 1) * (n + 1)) x d */
+  int                 works_owned;
+}
+sc_bspline_t;
+
+/** Compute the minimum required number of points for a certain degree.
+ * \param [in] n    Polynomial degree of the spline functions, n >= 0.
+ * \return          Return minimum point number = p + 1 >= n + 1.
+ */
+int                 sc_bspline_min_number_points (int n);
+
+/** Compute the minimum required number of knots for a certain degree.
+ * \param [in] n    Polynomial degree of the spline functions, n >= 0.
+ * \return          Return minimum knot number = m + 1 >= 2 * n + 2.
+ */
+int                 sc_bspline_min_number_knots (int n);
+
+/** Create a uniform B-spline knot vector.
+ * \param [in] n        Polynomial degree of the spline functions, n >= 0.
+ * \param [in] points   (p + 1) x d array of points in R^d, p >= 0, d >= 1.
+ * \return              (n + p + 2) x 1 array of knots.
+ */
+sc_dmatrix_t       *sc_bspline_knots_new (int n, sc_dmatrix_t * points);
+
+/** Create a B-spline knots array roughly proportional to the arc length.
+ * This works only for at least linear B-splines, n >= 1.
+ * \param [in] n        Polynomial degree of the spline functions, n >= 1.
+ * \param [in] points   (p + 1) x d array of points in R^d, p >= 0, d >= 1.
+ * \return              (n + p + 2) x 1 array of knots.
+ */
+sc_dmatrix_t       *sc_bspline_knots_new_length (int n,
+                                                 sc_dmatrix_t * points);
+
+/** Create a uniform B-spline knot vector for a periodic B-spline.
+ * \param [in] n        Polynomial degree of the spline functions, n >= 0.
+ * \param [in] points   (p + 1) x d array of points in R^d, p >= 0, d >= 1.
+ * \return              (n + p + 2) x 1 array of knots.
+ */
+sc_dmatrix_t       *sc_bspline_knots_new_periodic (int n,
+                                                   sc_dmatrix_t * points);
+
+/** Create a B-spline knots array roughly proportional to the arc length for a
+ * periodic B-spline.
+ * This works only for at least linear B-splines, n >= 1.
+ * \param [in] n        Polynomial degree of the spline functions, n >= 1.
+ * \param [in] points   (p + 1) x d array of points in R^d, p >= 0, d >= 1.
+ * \return              (n + p + 2) x 1 array of knots.
+ */
+sc_dmatrix_t       *sc_bspline_knots_new_length_periodic (int n,
+                                                          sc_dmatrix_t *
+                                                          points);
+
+/** Take a vector of points and make them appropriate for a periodic B-spine.
+ * \param [in] n          Polynomial degree of the spline functions, n >= 0.
+ * \param [in,out] points On input, an (l x d) array of points in R^d, l > 0,
+ *                        d >= 1.  If n is odd, these points are associated
+ *                        with the left endpoints of the intervals over
+ *                        which the B-spline is fully defined; if n is odd,
+ *                        they are associated with the midpoints.
+ *                        On output, points is a (l + n) x d, array of points
+ *                        appropriate for use as control points for a periodic
+ *                        B-spline.
+ */
+void                sc_bspline_make_points_periodic (int n, sc_dmatrix_t *
+                                                     points);
+
+/** Create workspace for B-spline evaluation.
+ * \param [in] n        Polynomial degree of the spline functions, n >= 0.
+ * \param [in] d        Dimension of the control points in R^d, d >= 1.
+ * \return              Workspace ((n + 1) * (n + 1)) x d.
+ */
+sc_dmatrix_t       *sc_bspline_workspace_new (int n, int d);
+
+/** Create a new B-spline structure.
+ * \param [in] n        Polynomial degree of the spline functions, n >= 0.
+ * \param [in] points   (p + 1) x d array of points in R^d, p >= 0, d >= 1.
+ *                      Borrowed, matrix is not copied so it must not be
+ *                      destroyed while the B-spline structure is in use.
+ * \param [in] knots    (n + p + 2) x 1 array of knots.  Borrowed.
+ *                      If NULL the knots are computed equidistantly.
+ * \param [in] works    Workspace ((n + 1) * (n + 1)) x d.  Borrowed.
+ *                      If NULL the workspace is allocated internally.
+ */
+sc_bspline_t       *sc_bspline_new (int n, sc_dmatrix_t * points,
+                                    sc_dmatrix_t * knots,
+                                    sc_dmatrix_t * works);
+
+/** Destroy a B-spline structure.
+ */
+void                sc_bspline_destroy (sc_bspline_t * bs);
+
+/** Evaluate a B-spline at a certain point.
+ * \param [in] bs       B-spline structure.
+ * \param [in] t        Value that must be within the range of the knots.
+ * \param [out] result  The computed point in R^d is placed here.
+ */
+void                sc_bspline_evaluate (sc_bspline_t * bs,
+                                         double t, double *result);
+
+/** Evaluate a B-spline derivative at a certain point.
+ * \param [in] bs       B-spline structure.
+ * \param [in] t        Value that must be within the range of the knots.
+ * \param [out] result  The computed derivative in R^d is placed here.
+ */
+void                sc_bspline_derivative (sc_bspline_t * bs,
+                                           double t, double *result);
+
+/** Evaluate any order B-spline derivative at a certain point.
+ * \param [in] bs       B-spline structure.
+ * \param [in] order    Order of the derivative >= 0.
+ * \param [in] t        Value that must be within the range of the knots.
+ * \param [out] result  The computed derivative in R^d is placed here.
+ */
+void                sc_bspline_derivative_n (sc_bspline_t * bs, int order,
+                                             double t, double *result);
+
+/** Evaluate a B-spline derivative at a certain point.  Obsolete.
+ * \param [in] bs       B-spline structure.
+ * \param [in] t        Value that must be within the range of the knots.
+ * \param [out] result  The computed derivative in R^d is placed here.
+ */
+void                sc_bspline_derivative2 (sc_bspline_t * bs,
+                                            double t, double *result);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_BSPLINE_H */
diff --git a/sc/src/sc_builtin/getopt.h b/sc/src/sc_builtin/getopt.h
new file mode 100644
index 0000000..b7a026c
--- /dev/null
+++ b/sc/src/sc_builtin/getopt.h
@@ -0,0 +1,177 @@
+/* Declarations for getopt.
+   Copyright (C) 1989-1994,1996-1999,2001,2003,2004
+   Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _GETOPT_H
+
+#ifndef __need_getopt
+# define _GETOPT_H 1
+#endif
+
+/* If __GNU_LIBRARY__ is not already defined, either we are being used
+   standalone, or this is the first header included in the source file.
+   If we are being used with glibc, we need to include <features.h>, but
+   that does not exist if we are standalone.  So: if __GNU_LIBRARY__ is
+   not defined, include <ctype.h>, which will pull in <features.h> for us
+   if it's from glibc.  (Why ctype.h?  It's guaranteed to exist and it
+   doesn't flood the namespace with stuff the way some other headers do.)  */
+#if !defined __GNU_LIBRARY__
+# include <ctype.h>
+#endif
+
+#ifndef __THROW
+# ifndef __GNUC_PREREQ
+#  define __GNUC_PREREQ(maj, min) (0)
+# endif
+# if defined __cplusplus && __GNUC_PREREQ (2,8)
+#  define __THROW	throw ()
+# else
+#  define __THROW
+# endif
+#endif
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+#ifndef __need_getopt
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument		(or 0) if the option does not take an argument,
+   required_argument	(or 1) if the option requires an argument,
+   optional_argument 	(or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+  const char *name;
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+# define no_argument		0
+# define required_argument	1
+# define optional_argument	2
+#endif	/* need getopt */
+
+
+/* Get definitions and prototypes for functions to process the
+   arguments in ARGV (ARGC of them, minus the program name) for
+   options given in OPTS.
+
+   Return the option character from OPTS just read.  Return -1 when
+   there are no more options.  For unrecognized options, or options
+   missing arguments, `optopt' is set to the option letter, and '?' is
+   returned.
+
+   The OPTS string is a list of characters which are recognized option
+   letters, optionally followed by colons, specifying that that letter
+   takes an argument, to be placed in `optarg'.
+
+   If a letter in OPTS is followed by two colons, its argument is
+   optional.  This behavior is specific to the GNU `getopt'.
+
+   The argument `--' causes premature termination of argument
+   scanning, explicitly telling `getopt' that there are no more
+   options.
+
+   If OPTS begins with `--', then non-option arguments are treated as
+   arguments to the option '\0'.  This behavior is specific to the GNU
+   `getopt'.  */
+
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+extern int getopt (int ___argc, char *const *___argv, const char *__shortopts)
+       __THROW;
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* __GNU_LIBRARY__ */
+
+#ifndef __need_getopt
+extern int getopt_long (int ___argc, char *const *___argv,
+			const char *__shortopts,
+		        const struct option *__longopts, int *__longind)
+       __THROW;
+extern int getopt_long_only (int ___argc, char *const *___argv,
+			     const char *__shortopts,
+		             const struct option *__longopts, int *__longind)
+       __THROW;
+
+#endif
+
+#ifdef	__cplusplus
+}
+#endif
+
+/* Make sure we later can get all the definitions and declarations.  */
+#undef __need_getopt
+
+#endif /* getopt.h */
diff --git a/sc/src/sc_builtin/getopt_int.h b/sc/src/sc_builtin/getopt_int.h
new file mode 100644
index 0000000..d982c72
--- /dev/null
+++ b/sc/src/sc_builtin/getopt_int.h
@@ -0,0 +1,130 @@
+/* Internal declarations for getopt.
+   Copyright (C) 1989-1994,1996-1999,2001,2003,2004
+   Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _GETOPT_INT_H
+#define _GETOPT_INT_H	1
+
+extern int _getopt_internal (int ___argc, char *const *___argv,
+			     const char *__shortopts,
+		             const struct option *__longopts, int *__longind,
+			     int __long_only);
+
+

+/* Reentrant versions which can handle parsing multiple argument
+   vectors at the same time.  */
+
+/* Data type for reentrant functions.  */
+struct _getopt_data
+{
+  /* These have exactly the same meaning as the corresponding global
+     variables, except that they are used for the reentrant
+     versions of getopt.  */
+  int optind;
+  int opterr;
+  int optopt;
+  char *optarg;
+
+  /* Internal members.  */
+
+  /* True if the internal members have been initialized.  */
+  int __initialized;
+
+  /* The next char to be scanned in the option-element
+     in which the last option character we returned was found.
+     This allows us to pick up the scan where we left off.
+
+     If this is zero, or a null string, it means resume the scan
+     by advancing to the next ARGV-element.  */
+  char *__nextchar;
+
+  /* Describe how to deal with options that follow non-option ARGV-elements.
+
+     If the caller did not specify anything,
+     the default is REQUIRE_ORDER if the environment variable
+     POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+     REQUIRE_ORDER means don't recognize them as options;
+     stop option processing when the first non-option is seen.
+     This is what Unix does.
+     This mode of operation is selected by either setting the environment
+     variable POSIXLY_CORRECT, or using `+' as the first character
+     of the list of option characters.
+
+     PERMUTE is the default.  We permute the contents of ARGV as we
+     scan, so that eventually all the non-options are at the end.
+     This allows options to be given in any order, even with programs
+     that were not written to expect this.
+
+     RETURN_IN_ORDER is an option available to programs that were
+     written to expect options and other ARGV-elements in any order
+     and that care about the ordering of the two.  We describe each
+     non-option ARGV-element as if it were the argument of an option
+     with character code 1.  Using `-' as the first character of the
+     list of option characters selects this mode of operation.
+
+     The special argument `--' forces an end of option-scanning regardless
+     of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+     `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+
+  enum
+    {
+      REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+    } __ordering;
+
+  /* If the POSIXLY_CORRECT environment variable is set.  */
+  int __posixly_correct;
+
+
+  /* Handle permutation of arguments.  */
+
+  /* Describe the part of ARGV that contains non-options that have
+     been skipped.  `first_nonopt' is the index in ARGV of the first
+     of them; `last_nonopt' is the index after the last of them.  */
+
+  int __first_nonopt;
+  int __last_nonopt;
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+  int __nonoption_flags_max_len;
+  int __nonoption_flags_len;
+# endif
+};
+
+/* The initializer is necessary to set OPTIND and OPTERR to their
+   default values and to clear the initialization flag.  */
+#define _GETOPT_DATA_INITIALIZER	{ 1, 1 }
+
+extern int _getopt_internal_r (int ___argc, char *const *___argv,
+			       const char *__shortopts,
+			       const struct option *__longopts, int *__longind,
+			       int __long_only, struct _getopt_data *__data);
+
+extern int _getopt_long_r (int ___argc, char *const *___argv,
+			   const char *__shortopts,
+			   const struct option *__longopts, int *__longind,
+			   struct _getopt_data *__data);
+
+extern int _getopt_long_only_r (int ___argc, char *const *___argv,
+				const char *__shortopts,
+				const struct option *__longopts,
+				int *__longind,
+				struct _getopt_data *__data);
+
+#endif /* getopt_int.h */
diff --git a/sc/src/sc_builtin/obstack.h b/sc/src/sc_builtin/obstack.h
new file mode 100644
index 0000000..206fe55
--- /dev/null
+++ b/sc/src/sc_builtin/obstack.h
@@ -0,0 +1,509 @@
+/* obstack.h - object stack macros
+   Copyright (C) 1988-1994,1996-1999,2003,2004,2005
+	Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* Summary:
+
+All the apparent functions defined here are macros. The idea
+is that you would use these pre-tested macros to solve a
+very specific set of problems, and they would run fast.
+Caution: no side-effects in arguments please!! They may be
+evaluated MANY times!!
+
+These macros operate a stack of objects.  Each object starts life
+small, and may grow to maturity.  (Consider building a word syllable
+by syllable.)  An object can move while it is growing.  Once it has
+been "finished" it never changes address again.  So the "top of the
+stack" is typically an immature growing object, while the rest of the
+stack is of mature, fixed size and fixed address objects.
+
+These routines grab large chunks of memory, using a function you
+supply, called `obstack_chunk_alloc'.  On occasion, they free chunks,
+by calling `obstack_chunk_free'.  You must define them and declare
+them before using any obstack macros.
+
+Each independent stack is represented by a `struct obstack'.
+Each of the obstack macros expects a pointer to such a structure
+as the first argument.
+
+One motivation for this package is the problem of growing char strings
+in symbol tables.  Unless you are "fascist pig with a read-only mind"
+--Gosper's immortal quote from HAKMEM item 154, out of context--you
+would not like to put any arbitrary upper limit on the length of your
+symbols.
+
+In practice this often means you will build many short symbols and a
+few long symbols.  At the time you are reading a symbol you don't know
+how long it is.  One traditional method is to read a symbol into a
+buffer, realloc()ating the buffer every time you try to read a symbol
+that is longer than the buffer.  This is beaut, but you still will
+want to copy the symbol from the buffer to a more permanent
+symbol-table entry say about half the time.
+
+With obstacks, you can work differently.  Use one obstack for all symbol
+names.  As you read a symbol, grow the name in the obstack gradually.
+When the name is complete, finalize it.  Then, if the symbol exists already,
+free the newly read name.
+
+The way we do this is to take a large chunk, allocating memory from
+low addresses.  When you want to build a symbol in the chunk you just
+add chars above the current "high water mark" in the chunk.  When you
+have finished adding chars, because you got to the end of the symbol,
+you know how long the chars are, and you can create a new object.
+Mostly the chars will not burst over the highest address of the chunk,
+because you would typically expect a chunk to be (say) 100 times as
+long as an average object.
+
+In case that isn't clear, when we have enough chars to make up
+the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
+so we just point to it where it lies.  No moving of chars is
+needed and this is the second win: potentially long strings need
+never be explicitly shuffled. Once an object is formed, it does not
+change its address during its lifetime.
+
+When the chars burst over a chunk boundary, we allocate a larger
+chunk, and then copy the partly formed object from the end of the old
+chunk to the beginning of the new larger chunk.  We then carry on
+accreting characters to the end of the object as we normally would.
+
+A special macro is provided to add a single char at a time to a
+growing object.  This allows the use of register variables, which
+break the ordinary 'growth' macro.
+
+Summary:
+	We allocate large chunks.
+	We carve out one object at a time from the current chunk.
+	Once carved, an object never moves.
+	We are free to append data of any size to the currently
+	  growing object.
+	Exactly one object is growing in an obstack at any one time.
+	You can run one obstack per control block.
+	You may have as many control blocks as you dare.
+	Because of the way we do it, you can `unwind' an obstack
+	  back to a previous state. (You may remove objects much
+	  as you would with a stack.)
+*/
+
+
+/* Don't do the contents of this file more than once.  */
+
+#ifndef _OBSTACK_H
+#define _OBSTACK_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+

+/* We need the type of a pointer subtraction.  If __PTRDIFF_TYPE__ is
+   defined, as with GNU C, use that; that way we don't pollute the
+   namespace with <stddef.h>'s symbols.  Otherwise, include <stddef.h>
+   and use ptrdiff_t.  */
+
+#ifdef __PTRDIFF_TYPE__
+# define PTR_INT_TYPE __PTRDIFF_TYPE__
+#else
+# include <stddef.h>
+# define PTR_INT_TYPE ptrdiff_t
+#endif
+
+/* If B is the base of an object addressed by P, return the result of
+   aligning P to the next multiple of A + 1.  B and P must be of type
+   char *.  A + 1 must be a power of 2.  */
+
+#define __BPTR_ALIGN(B, P, A) ((B) + (((P) - (B) + (A)) & ~(A)))
+
+/* Similiar to _BPTR_ALIGN (B, P, A), except optimize the common case
+   where pointers can be converted to integers, aligned as integers,
+   and converted back again.  If PTR_INT_TYPE is narrower than a
+   pointer (e.g., the AS/400), play it safe and compute the alignment
+   relative to B.  Otherwise, use the faster strategy of computing the
+   alignment relative to 0.  */
+
+#define __PTR_ALIGN(B, P, A)						    \
+  __BPTR_ALIGN (sizeof (PTR_INT_TYPE) < sizeof (void *) ? (B) : (char *) 0, \
+		P, A)
+
+#include <string.h>
+
+struct _obstack_chunk		/* Lives at front of each chunk. */
+{
+  char  *limit;			/* 1 past end of this chunk */
+  struct _obstack_chunk *prev;	/* address of prior chunk or NULL */
+  char	contents[4];		/* objects begin here */
+};
+
+struct obstack		/* control current object in current chunk */
+{
+  long	chunk_size;		/* preferred size to allocate chunks in */
+  struct _obstack_chunk *chunk;	/* address of current struct obstack_chunk */
+  char	*object_base;		/* address of object we are building */
+  char	*next_free;		/* where to add next char to current object */
+  char	*chunk_limit;		/* address of char after current chunk */
+  union
+  {
+    PTR_INT_TYPE tempint;
+    void *tempptr;
+  } temp;			/* Temporary for some macros.  */
+  int   alignment_mask;		/* Mask of alignment for each object. */
+  /* These prototypes vary based on `use_extra_arg', and we use
+     casts to the prototypeless function type in all assignments,
+     but having prototypes here quiets -Wstrict-prototypes.  */
+  struct _obstack_chunk *(*chunkfun) (void *, long);
+  void (*freefun) (void *, struct _obstack_chunk *);
+  void *extra_arg;		/* first arg for chunk alloc/dealloc funcs */
+  unsigned use_extra_arg:1;	/* chunk alloc/dealloc funcs take extra arg */
+  unsigned maybe_empty_object:1;/* There is a possibility that the current
+				   chunk contains a zero-length object.  This
+				   prevents freeing the chunk if we allocate
+				   a bigger chunk to replace it. */
+  unsigned alloc_failed:1;	/* No longer used, as we now call the failed
+				   handler on error, but retained for binary
+				   compatibility.  */
+};
+
+/* Declare the external functions we use; they are in obstack.c.  */
+
+extern void _obstack_newchunk (struct obstack *, int);
+extern int _obstack_begin (struct obstack *, int, int,
+			    void *(*) (long), void (*) (void *));
+extern int _obstack_begin_1 (struct obstack *, int, int,
+			     void *(*) (void *, long),
+			     void (*) (void *, void *), void *);
+extern int _obstack_memory_used (struct obstack *);
+
+void obstack_free (struct obstack *obstack, void *block);
+
+

+/* Error handler called when `obstack_chunk_alloc' failed to allocate
+   more memory.  This can be set to a user defined function which
+   should either abort gracefully or use longjump - but shouldn't
+   return.  The default action is to print a message and abort.  */
+extern void (*obstack_alloc_failed_handler) (void);
+
+/* Exit value used when `print_and_abort' is used.  */
+extern int obstack_exit_failure;
+

+/* Pointer to beginning of object being allocated or to be allocated next.
+   Note that this might not be the final address of the object
+   because a new chunk might be needed to hold the final size.  */
+
+#define obstack_base(h) ((void *) (h)->object_base)
+
+/* Size for allocating ordinary chunks.  */
+
+#define obstack_chunk_size(h) ((h)->chunk_size)
+
+/* Pointer to next byte not yet allocated in current chunk.  */
+
+#define obstack_next_free(h)	((h)->next_free)
+
+/* Mask specifying low bits that should be clear in address of an object.  */
+
+#define obstack_alignment_mask(h) ((h)->alignment_mask)
+
+/* To prevent prototype warnings provide complete argument list.  */
+#define obstack_init(h)						\
+  _obstack_begin ((h), 0, 0,					\
+		  (void *(*) (long)) obstack_chunk_alloc,	\
+		  (void (*) (void *)) obstack_chunk_free)
+
+#define obstack_begin(h, size)					\
+  _obstack_begin ((h), (size), 0,				\
+		  (void *(*) (long)) obstack_chunk_alloc,	\
+		  (void (*) (void *)) obstack_chunk_free)
+
+#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun)  \
+  _obstack_begin ((h), (size), (alignment),				   \
+		  (void *(*) (long)) (chunkfun),			   \
+		  (void (*) (void *)) (freefun))
+
+#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \
+  _obstack_begin_1 ((h), (size), (alignment),				\
+		    (void *(*) (void *, long)) (chunkfun),		\
+		    (void (*) (void *, void *)) (freefun), (arg))
+
+#define obstack_chunkfun(h, newchunkfun) \
+  ((h) -> chunkfun = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))
+
+#define obstack_freefun(h, newfreefun) \
+  ((h) -> freefun = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))
+
+#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = (achar))
+
+#define obstack_blank_fast(h,n) ((h)->next_free += (n))
+
+#define obstack_memory_used(h) _obstack_memory_used (h)
+

+#if defined __GNUC__ && defined __STDC__ && __STDC__
+/* NextStep 2.0 cc is really gcc 1.93 but it defines __GNUC__ = 2 and
+   does not implement __extension__.  But that compiler doesn't define
+   __GNUC_MINOR__.  */
+# if __GNUC__ < 2 || (__NeXT__ && !__GNUC_MINOR__)
+#  define __extension__
+# endif
+
+/* For GNU C, if not -traditional,
+   we can define these macros to compute all args only once
+   without using a global variable.
+   Also, we can avoid using the `temp' slot, to make faster code.  */
+
+# define obstack_object_size(OBSTACK)					\
+  __extension__								\
+  ({ struct obstack const *__o = (OBSTACK);				\
+     (unsigned) (__o->next_free - __o->object_base); })
+
+# define obstack_room(OBSTACK)						\
+  __extension__								\
+  ({ struct obstack const *__o = (OBSTACK);				\
+     (unsigned) (__o->chunk_limit - __o->next_free); })
+
+# define obstack_make_room(OBSTACK,length)				\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   int __len = (length);						\
+   if (__o->chunk_limit - __o->next_free < __len)			\
+     _obstack_newchunk (__o, __len);					\
+   (void) 0; })
+
+# define obstack_empty_p(OBSTACK)					\
+  __extension__								\
+  ({ struct obstack const *__o = (OBSTACK);				\
+     (__o->chunk->prev == 0						\
+      && __o->next_free == __PTR_ALIGN ((char *) __o->chunk,		\
+					__o->chunk->contents,		\
+					__o->alignment_mask)); })
+
+# define obstack_grow(OBSTACK,where,length)				\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   int __len = (length);						\
+   if (__o->next_free + __len > __o->chunk_limit)			\
+     _obstack_newchunk (__o, __len);					\
+   memcpy (__o->next_free, where, __len);				\
+   __o->next_free += __len;						\
+   (void) 0; })
+
+# define obstack_grow0(OBSTACK,where,length)				\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   int __len = (length);						\
+   if (__o->next_free + __len + 1 > __o->chunk_limit)			\
+     _obstack_newchunk (__o, __len + 1);				\
+   memcpy (__o->next_free, where, __len);				\
+   __o->next_free += __len;						\
+   *(__o->next_free)++ = 0;						\
+   (void) 0; })
+
+# define obstack_1grow(OBSTACK,datum)					\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   if (__o->next_free + 1 > __o->chunk_limit)				\
+     _obstack_newchunk (__o, 1);					\
+   obstack_1grow_fast (__o, datum);					\
+   (void) 0; })
+
+/* These assume that the obstack alignment is good enough for pointers
+   or ints, and that the data added so far to the current object
+   shares that much alignment.  */
+
+# define obstack_ptr_grow(OBSTACK,datum)				\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   if (__o->next_free + sizeof (void *) > __o->chunk_limit)		\
+     _obstack_newchunk (__o, sizeof (void *));				\
+   obstack_ptr_grow_fast (__o, datum); })				\
+
+# define obstack_int_grow(OBSTACK,datum)				\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   if (__o->next_free + sizeof (int) > __o->chunk_limit)		\
+     _obstack_newchunk (__o, sizeof (int));				\
+   obstack_int_grow_fast (__o, datum); })
+
+# define obstack_ptr_grow_fast(OBSTACK,aptr)				\
+__extension__								\
+({ struct obstack *__o1 = (OBSTACK);					\
+   *(const void **) __o1->next_free = (aptr);				\
+   __o1->next_free += sizeof (const void *);				\
+   (void) 0; })
+
+# define obstack_int_grow_fast(OBSTACK,aint)				\
+__extension__								\
+({ struct obstack *__o1 = (OBSTACK);					\
+   *(int *) __o1->next_free = (aint);					\
+   __o1->next_free += sizeof (int);					\
+   (void) 0; })
+
+# define obstack_blank(OBSTACK,length)					\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   int __len = (length);						\
+   if (__o->chunk_limit - __o->next_free < __len)			\
+     _obstack_newchunk (__o, __len);					\
+   obstack_blank_fast (__o, __len);					\
+   (void) 0; })
+
+# define obstack_alloc(OBSTACK,length)					\
+__extension__								\
+({ struct obstack *__h = (OBSTACK);					\
+   obstack_blank (__h, (length));					\
+   obstack_finish (__h); })
+
+# define obstack_copy(OBSTACK,where,length)				\
+__extension__								\
+({ struct obstack *__h = (OBSTACK);					\
+   obstack_grow (__h, (where), (length));				\
+   obstack_finish (__h); })
+
+# define obstack_copy0(OBSTACK,where,length)				\
+__extension__								\
+({ struct obstack *__h = (OBSTACK);					\
+   obstack_grow0 (__h, (where), (length));				\
+   obstack_finish (__h); })
+
+/* The local variable is named __o1 to avoid a name conflict
+   when obstack_blank is called.  */
+# define obstack_finish(OBSTACK)					\
+__extension__								\
+({ struct obstack *__o1 = (OBSTACK);					\
+   void *__value = (void *) __o1->object_base;				\
+   if (__o1->next_free == __value)					\
+     __o1->maybe_empty_object = 1;					\
+   __o1->next_free							\
+     = __PTR_ALIGN (__o1->object_base, __o1->next_free,			\
+		    __o1->alignment_mask);				\
+   if (__o1->next_free - (char *)__o1->chunk				\
+       > __o1->chunk_limit - (char *)__o1->chunk)			\
+     __o1->next_free = __o1->chunk_limit;				\
+   __o1->object_base = __o1->next_free;					\
+   __value; })
+
+# define obstack_free(OBSTACK, OBJ)					\
+__extension__								\
+({ struct obstack *__o = (OBSTACK);					\
+   void *__obj = (OBJ);							\
+   if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit)  \
+     __o->next_free = __o->object_base = (char *)__obj;			\
+   else (obstack_free) (__o, __obj); })
+

+#else /* not __GNUC__ or not __STDC__ */
+
+# define obstack_object_size(h) \
+ (unsigned) ((h)->next_free - (h)->object_base)
+
+# define obstack_room(h)		\
+ (unsigned) ((h)->chunk_limit - (h)->next_free)
+
+# define obstack_empty_p(h) \
+ ((h)->chunk->prev == 0							\
+  && (h)->next_free == __PTR_ALIGN ((char *) (h)->chunk,		\
+				    (h)->chunk->contents,		\
+				    (h)->alignment_mask))
+
+/* Note that the call to _obstack_newchunk is enclosed in (..., 0)
+   so that we can avoid having void expressions
+   in the arms of the conditional expression.
+   Casting the third operand to void was tried before,
+   but some compilers won't accept it.  */
+
+# define obstack_make_room(h,length)					\
+( (h)->temp.tempint = (length),						\
+  (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit)		\
+   ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0))
+
+# define obstack_grow(h,where,length)					\
+( (h)->temp.tempint = (length),						\
+  (((h)->next_free + (h)->temp.tempint > (h)->chunk_limit)		\
+   ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0),		\
+  memcpy ((h)->next_free, where, (h)->temp.tempint),			\
+  (h)->next_free += (h)->temp.tempint)
+
+# define obstack_grow0(h,where,length)					\
+( (h)->temp.tempint = (length),						\
+  (((h)->next_free + (h)->temp.tempint + 1 > (h)->chunk_limit)		\
+   ? (_obstack_newchunk ((h), (h)->temp.tempint + 1), 0) : 0),		\
+  memcpy ((h)->next_free, where, (h)->temp.tempint),			\
+  (h)->next_free += (h)->temp.tempint,					\
+  *((h)->next_free)++ = 0)
+
+# define obstack_1grow(h,datum)						\
+( (((h)->next_free + 1 > (h)->chunk_limit)				\
+   ? (_obstack_newchunk ((h), 1), 0) : 0),				\
+  obstack_1grow_fast (h, datum))
+
+# define obstack_ptr_grow(h,datum)					\
+( (((h)->next_free + sizeof (char *) > (h)->chunk_limit)		\
+   ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0),		\
+  obstack_ptr_grow_fast (h, datum))
+
+# define obstack_int_grow(h,datum)					\
+( (((h)->next_free + sizeof (int) > (h)->chunk_limit)			\
+   ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0),			\
+  obstack_int_grow_fast (h, datum))
+
+# define obstack_ptr_grow_fast(h,aptr)					\
+  (((const void **) ((h)->next_free += sizeof (void *)))[-1] = (aptr))
+
+# define obstack_int_grow_fast(h,aint)					\
+  (((int *) ((h)->next_free += sizeof (int)))[-1] = (aint))
+
+# define obstack_blank(h,length)					\
+( (h)->temp.tempint = (length),						\
+  (((h)->chunk_limit - (h)->next_free < (h)->temp.tempint)		\
+   ? (_obstack_newchunk ((h), (h)->temp.tempint), 0) : 0),		\
+  obstack_blank_fast (h, (h)->temp.tempint))
+
+# define obstack_alloc(h,length)					\
+ (obstack_blank ((h), (length)), obstack_finish ((h)))
+
+# define obstack_copy(h,where,length)					\
+ (obstack_grow ((h), (where), (length)), obstack_finish ((h)))
+
+# define obstack_copy0(h,where,length)					\
+ (obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))
+
+# define obstack_finish(h)						\
+( ((h)->next_free == (h)->object_base					\
+   ? (((h)->maybe_empty_object = 1), 0)					\
+   : 0),								\
+  (h)->temp.tempptr = (h)->object_base,					\
+  (h)->next_free							\
+    = __PTR_ALIGN ((h)->object_base, (h)->next_free,			\
+		   (h)->alignment_mask),				\
+  (((h)->next_free - (char *) (h)->chunk				\
+    > (h)->chunk_limit - (char *) (h)->chunk)				\
+   ? ((h)->next_free = (h)->chunk_limit) : 0),				\
+  (h)->object_base = (h)->next_free,					\
+  (h)->temp.tempptr)
+
+# define obstack_free(h,obj)						\
+( (h)->temp.tempint = (char *) (obj) - (char *) (h)->chunk,		\
+  ((((h)->temp.tempint > 0						\
+    && (h)->temp.tempint < (h)->chunk_limit - (char *) (h)->chunk))	\
+   ? (int) ((h)->next_free = (h)->object_base				\
+	    = (h)->temp.tempint + (char *) (h)->chunk)			\
+   : (((obstack_free) ((h), (h)->temp.tempint + (char *) (h)->chunk), 0), 0)))
+
+#endif /* not __GNUC__ or not __STDC__ */
+
+#ifdef __cplusplus
+}	/* C++ */
+#endif
+
+#endif /* obstack.h */
diff --git a/sc/src/sc_containers.c b/sc/src/sc_containers.c
new file mode 100644
index 0000000..89de654
--- /dev/null
+++ b/sc/src/sc_containers.c
@@ -0,0 +1,1478 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_containers.h>
+#ifdef SC_HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+/* array routines */
+
+size_t
+sc_array_memory_used (sc_array_t * array, int is_dynamic)
+{
+  return (is_dynamic ? sizeof (sc_array_t) : 0) +
+    (SC_ARRAY_IS_OWNER (array) ? array->byte_alloc : 0);
+}
+
+sc_array_t         *
+sc_array_new (size_t elem_size)
+{
+  sc_array_t         *array;
+
+  array = SC_ALLOC (sc_array_t, 1);
+
+  sc_array_init (array, elem_size);
+
+  return array;
+}
+
+sc_array_t         *
+sc_array_new_size (size_t elem_size, size_t elem_count)
+{
+  sc_array_t         *array;
+
+  array = SC_ALLOC (sc_array_t, 1);
+
+  sc_array_init_size (array, elem_size, elem_count);
+
+  return array;
+}
+
+sc_array_t         *
+sc_array_new_view (sc_array_t * array, size_t offset, size_t length)
+{
+  sc_array_t         *view;
+
+  view = SC_ALLOC (sc_array_t, 1);
+
+  sc_array_init_view (view, array, offset, length);
+
+  return view;
+}
+
+sc_array_t         *
+sc_array_new_data (void *base, size_t elem_size, size_t elem_count)
+{
+  sc_array_t         *view;
+
+  view = SC_ALLOC (sc_array_t, 1);
+
+  sc_array_init_data (view, base, elem_size, elem_count);
+
+  return view;
+}
+
+void
+sc_array_destroy (sc_array_t * array)
+{
+  if (SC_ARRAY_IS_OWNER (array)) {
+    SC_FREE (array->array);
+  }
+  SC_FREE (array);
+}
+
+void
+sc_array_init (sc_array_t * array, size_t elem_size)
+{
+  SC_ASSERT (elem_size > 0);
+
+  array->elem_size = elem_size;
+  array->elem_count = 0;
+  array->byte_alloc = 0;
+  array->array = NULL;
+}
+
+void
+sc_array_init_size (sc_array_t * array, size_t elem_size, size_t elem_count)
+{
+  SC_ASSERT (elem_size > 0);
+
+  array->elem_size = elem_size;
+  array->elem_count = elem_count;
+  array->byte_alloc = (ssize_t) (elem_size * elem_count);
+  array->array = SC_ALLOC (char, (size_t) array->byte_alloc);
+}
+
+void
+sc_array_init_view (sc_array_t * view, sc_array_t * array, size_t offset,
+                    size_t length)
+{
+  SC_ASSERT (offset + length <= array->elem_count);
+
+  view->elem_size = array->elem_size;
+  view->elem_count = length;
+  view->byte_alloc = -(ssize_t) (length * array->elem_size + 1);
+  view->array = array->array + offset * array->elem_size;
+}
+
+void
+sc_array_init_data (sc_array_t * view, void *base, size_t elem_size,
+                    size_t elem_count)
+{
+  SC_ASSERT (elem_size > 0);
+
+  view->elem_size = elem_size;
+  view->elem_count = elem_count;
+  view->byte_alloc = -(ssize_t) (elem_count * elem_size + 1);
+  view->array = (char *) base;
+}
+
+void
+sc_array_reset (sc_array_t * array)
+{
+  if (SC_ARRAY_IS_OWNER (array)) {
+    SC_FREE (array->array);
+  }
+  array->array = NULL;
+
+  array->elem_count = 0;
+  array->byte_alloc = 0;
+}
+
+void
+sc_array_truncate (sc_array_t * array)
+{
+  SC_ASSERT (SC_ARRAY_IS_OWNER (array));
+
+  array->elem_count = 0;
+
+#if SC_DEBUG
+  SC_ASSERT (array->byte_alloc >= 0);
+  memset (array->array, -1, array->byte_alloc);
+#endif
+}
+
+void
+sc_array_resize (sc_array_t * array, size_t new_count)
+{
+  size_t              newoffs, roundup, newsize;
+#ifdef SC_DEBUG
+  size_t              oldoffs;
+  size_t              i, minoffs;
+#endif
+
+  if (!SC_ARRAY_IS_OWNER (array)) {
+    /* *INDENT-OFF* HORRIBLE indent bug */
+    SC_ASSERT (new_count * array->elem_size <=
+               (size_t) -(array->byte_alloc + 1));
+    /* *INDENT-ON* */
+    array->elem_count = new_count;
+    return;
+  }
+
+  /* We know that this array is not a view now so we can call reset. */
+  if (new_count == 0) {
+    sc_array_reset (array);
+    return;
+  }
+
+#ifdef SC_DEBUG
+  oldoffs = array->elem_count * array->elem_size;
+#endif
+  array->elem_count = new_count;
+  newoffs = array->elem_count * array->elem_size;
+  roundup = (size_t) SC_ROUNDUP2_64 (newoffs);
+  SC_ASSERT (roundup >= newoffs && roundup <= 2 * newoffs);
+
+  if (newoffs > (size_t) array->byte_alloc ||
+      roundup < (size_t) array->byte_alloc) {
+    array->byte_alloc = (ssize_t) roundup;
+  }
+  else {
+#ifdef SC_DEBUG
+    if (newoffs < oldoffs) {
+      memset (array->array + newoffs, -1, oldoffs - newoffs);
+    }
+    for (i = oldoffs; i < newoffs; ++i) {
+      SC_ASSERT (array->array[i] == (char) -1);
+    }
+#endif
+    return;
+  }
+  SC_ASSERT ((size_t) array->byte_alloc >= newoffs);
+
+  newsize = (size_t) array->byte_alloc;
+  array->array = SC_REALLOC (array->array, char, newsize);
+
+#ifdef SC_DEBUG
+  minoffs = SC_MIN (oldoffs, newoffs);
+  SC_ASSERT (minoffs <= newsize);
+  memset (array->array + minoffs, -1, newsize - minoffs);
+#endif
+}
+
+void
+sc_array_copy (sc_array_t * dest, sc_array_t * src)
+{
+  SC_ASSERT (SC_ARRAY_IS_OWNER (dest));
+  SC_ASSERT (dest->elem_size == src->elem_size);
+
+  sc_array_resize (dest, src->elem_count);
+  memcpy (dest->array, src->array, src->elem_count * src->elem_size);
+}
+
+void
+sc_array_sort (sc_array_t * array, int (*compar) (const void *, const void *))
+{
+  qsort (array->array, array->elem_count, array->elem_size, compar);
+}
+
+int
+sc_array_is_sorted (sc_array_t * array,
+                    int (*compar) (const void *, const void *))
+{
+  const size_t        count = array->elem_count;
+  size_t              zz;
+  void               *vold, *vnew;
+
+  if (count <= 1) {
+    return 1;
+  }
+
+  vold = sc_array_index (array, 0);
+  for (zz = 1; zz < count; ++zz) {
+    vnew = sc_array_index (array, zz);
+    if (compar (vold, vnew) > 0) {
+      return 0;
+    }
+    vold = vnew;
+  }
+
+  return 1;
+}
+
+int
+sc_array_is_equal (sc_array_t * array, sc_array_t * other)
+{
+  if (array->elem_size != other->elem_size ||
+      array->elem_count != other->elem_count) {
+    return 0;
+  }
+  return !memcmp (array->array, other->array,
+                  array->elem_size * array->elem_count);
+}
+
+void
+sc_array_uniq (sc_array_t * array, int (*compar) (const void *, const void *))
+{
+  size_t              incount, dupcount;
+  size_t              i, j;
+  void               *elem1, *elem2, *temp;
+
+  SC_ASSERT (SC_ARRAY_IS_OWNER (array));
+
+  incount = array->elem_count;
+  if (incount == 0) {
+    return;
+  }
+
+  dupcount = 0;                 /* count duplicates */
+  i = 0;                        /* read counter */
+  j = 0;                        /* write counter */
+  elem1 = sc_array_index (array, 0);
+  while (i < incount) {
+    elem2 = ((i < incount - 1) ? sc_array_index (array, i + 1) : NULL);
+    if (i < incount - 1 && compar (elem1, elem2) == 0) {
+      ++dupcount;
+      ++i;
+    }
+    else {
+      if (i > j) {
+        temp = sc_array_index (array, j);
+        memcpy (temp, elem1, array->elem_size);
+      }
+      ++i;
+      ++j;
+    }
+    elem1 = elem2;
+  }
+  SC_ASSERT (i == incount);
+  SC_ASSERT (j + dupcount == incount);
+  sc_array_resize (array, j);
+}
+
+ssize_t
+sc_array_bsearch (sc_array_t * array, const void *key,
+                  int (*compar) (const void *, const void *))
+{
+  ssize_t             is = -1;
+  char               *retval;
+
+  retval = (char *)
+    bsearch (key, array->array, array->elem_count, array->elem_size, compar);
+
+  if (retval != NULL) {
+    is = (ssize_t) ((retval - array->array) / array->elem_size);
+    SC_ASSERT (is >= 0 && is < (ssize_t) array->elem_count);
+  }
+
+  return is;
+}
+
+void
+sc_array_split (sc_array_t * array, sc_array_t * offsets, size_t num_types,
+                sc_array_type_t type_fn, void *data)
+{
+  const size_t        count = array->elem_count;
+  size_t              zi, *zp;
+  size_t              guess, low, high, type, step;
+
+  SC_ASSERT (offsets->elem_size == sizeof (size_t));
+
+  sc_array_resize (offsets, num_types + 1);
+
+  /** The point of this algorithm is to put offsets[i] into its final position
+   * for i = 0,...,num_types, where the final position of offsets[i] is the
+   * unique index k such that type_fn (array, j, data) < i for all j < k
+   * and type_fn (array, j, data) >= i for all j >= k.
+   *
+   * The invariants of the loop are:
+   *  1) if i < step, then offsets[i] <= low, and offsets[i] is final.
+   *  2) if i >= step, then low is less than or equal to the final value of
+   *     offsets[i].
+   *  3) for 0 <= i <= num_types, offsets[i] is greater than or equal to its
+   *     final value.
+   *  4) for every index k in the array with k < low,
+   *     type_fn (array, k, data) < step,
+   *  5) for 0 <= i < num_types,
+   *     for every index k in the array with k >= offsets[i],
+   *     type_fn (array, k, data) >= i.
+   *  6) if i < j, offsets[i] <= offsets[j].
+   *
+   * Initializing offsets[0] = 0, offsets[i] = count for i > 0,
+   * low = 0, and step = 1, the invariants are trivially satisfied.
+   */
+  zp = (size_t *) sc_array_index (offsets, 0);
+  *zp = 0;
+  for (zi = 1; zi <= num_types; zi++) {
+    zp = (size_t *) sc_array_index (offsets, zi);
+    *zp = count;
+  }
+
+  if (count == 0 || num_types <= 1) {
+    return;
+  }
+
+  /** Because count > 0 we can add another invariant:
+   *   7) if step < num_types, low < high = offsets[step].
+   */
+
+  low = 0;
+  high = count;                 /* high = offsets[step] */
+  step = 1;
+  for (;;) {
+    guess = low + (high - low) / 2;     /* By (7) low <= guess < high. */
+    type = type_fn (array, guess, data);
+    SC_ASSERT (type < num_types);
+    /** If type < step, then we can set low = guess + 1 and still satisfy
+     * invariant (4).  Also, because guess < high, we are assured low <= high.
+     */
+    if (type < step) {
+      low = guess + 1;
+    }
+    /** If type >= step, then setting offsets[i] = guess for i = step,..., type
+     * still satisfies invariant (5).  Because guess >= low, we are assured
+     * low <= high, and we maintain invariant (6).
+     */
+    else {
+      for (zi = step; zi <= type; zi++) {
+        zp = (size_t *) sc_array_index (offsets, zi);
+        *zp = guess;
+      }
+      high = guess;             /* high = offsets[step] */
+    }
+    /** If low = (high = offsets[step]), then by invariants (2) and (3)
+     * offsets[step] is in its final position, so we can increment step and
+     * still satisfy invariant (1).
+     */
+    while (low == high) {
+      /* By invariant (6), high cannot decrease here */
+      ++step;                   /* sc_array_index might be a macro */
+      high = *((size_t *) sc_array_index (offsets, step));
+      /** If step = num_types, then by invariant (1) we have found the final
+       * positions for offsets[i] for i < num_types, and offsets[num_types] =
+       * count in all situations, so we are done.
+       */
+      if (step == num_types) {
+        return;
+      }
+    }
+    /** To reach this point it must be true that low < high, so we preserve
+     * invariant (7).
+     */
+  }
+}
+
+int
+sc_array_is_permutation (sc_array_t * newindices)
+{
+  size_t              count = newindices->elem_count;
+  int                *counted = SC_ALLOC_ZERO (int, count);
+  size_t              zi;
+  size_t              zj;
+  size_t             *newind;
+
+  SC_ASSERT (newindices->elem_size == sizeof (size_t));
+  if (!newindices->elem_count) {
+    SC_FREE (counted);
+    return 1;
+  }
+  newind = (size_t *) sc_array_index (newindices, 0);
+
+  for (zi = 0; zi < count; zi++) {
+    zj = newind[zi];
+    if (zj >= count) {
+      SC_FREE (counted);
+      return 0;
+    }
+    counted[zj]++;
+  }
+
+  for (zi = 0; zi < count; zi++) {
+    if (counted[zi] != 1) {
+      SC_FREE (counted);
+      return 0;
+    }
+  }
+
+  SC_FREE (counted);
+  return 1;
+}
+
+/** permute an array in place.  newind[i] is the new index for the data that
+ * is currently at index i. entries in newind will be altered by this
+ * procedure */
+void
+sc_array_permute (sc_array_t * array, sc_array_t * newindices, int keepperm)
+{
+  size_t              zi, zj, zk;
+  char               *temp = SC_ALLOC (char, array->elem_size);
+  char               *carray = array->array;
+  size_t              esize = array->elem_size * sizeof (char);
+  size_t              count = array->elem_count;
+  size_t             *newind;
+
+  SC_ASSERT (newindices->elem_size == sizeof (size_t));
+  SC_ASSERT (newindices->elem_count == count);
+  SC_ASSERT (sc_array_is_permutation (newindices));
+  if (!count) {
+    SC_FREE (temp);
+    return;
+  }
+
+  if (!keepperm) {
+    newind = (size_t *) sc_array_index (newindices, 0);
+  }
+  else {
+    newind = SC_ALLOC (size_t, count);
+    memcpy (newind, sc_array_index (newindices, 0), count * sizeof (size_t));
+  }
+
+  zi = 0;
+  zj = 0;
+
+  while (zi < count) {
+    /* zi is the index of the current pivot slot */
+    /* zj is the old index of the data in the pivot */
+    /* zk is the new index for what is in the pivot */
+    zk = newind[zj];
+    SC_ASSERT (zk < count);
+    while (zk != zi) {
+      /* vacate zk */
+      memcpy (temp, carray + esize * zk, esize);
+      /* copy pivot to zk */
+      memcpy (carray + esize * zk, carray + esize * zi, esize);
+      /* copy zk to pivot */
+      memcpy (carray + esize * zi, temp, esize);
+      /* what was in zk is now in the pivot zi */
+      zj = zk;
+      zk = newind[zk];
+      SC_ASSERT (zk < count);
+      /* change newind to reflect the fact that what is now in zj is what is
+       * supposed to be in zj */
+      newind[zj] = zj;
+    }
+    newind[zi] = zi;
+    zj = (++zi);
+  }
+
+  if (keepperm) {
+    SC_FREE (newind);
+  }
+
+  SC_FREE (temp);
+}
+
+unsigned
+sc_array_checksum (sc_array_t * array)
+{
+#ifdef SC_HAVE_ZLIB
+  uInt                bytes;
+  uLong               crc;
+
+  crc = adler32 (0L, Z_NULL, 0);
+  if (array->elem_count == 0) {
+    return (unsigned) crc;
+  }
+
+  bytes = (uInt) (array->elem_count * array->elem_size);
+  crc = adler32 (crc, (const Bytef *) array->array, bytes);
+
+  return (unsigned) crc;
+#else
+  SC_ABORT ("Configure did not find a recent enough zlib.  Abort.\n");
+
+  return 0;
+#endif
+}
+
+size_t
+sc_array_pqueue_add (sc_array_t * array, void *temp,
+                     int (*compar) (const void *, const void *))
+{
+  int                 comp;
+  size_t              parent, child, swaps;
+  const size_t        size = array->elem_size;
+  void               *p, *c;
+
+  /* this works on a pre-allocated array that is not a view */
+  SC_ASSERT (SC_ARRAY_IS_OWNER (array));
+  SC_ASSERT (array->elem_count > 0);
+
+  /* PQUEUE FUNCTIONS ARE UNTESTED AND CURRENTLY DISABLED. */
+  SC_ABORT_NOT_REACHED ();
+
+  swaps = 0;
+  child = array->elem_count - 1;
+  c = array->array + (size * child);
+  while (child > 0) {
+    parent = (child - 1) / 2;
+    p = array->array + (size * parent);
+
+    /* compare child to parent */
+    comp = compar (p, c);
+    if (comp <= 0) {
+      break;
+    }
+
+    /* swap child and parent */
+    memcpy (temp, c, size);
+    memcpy (c, p, size);
+    memcpy (p, temp, size);
+    ++swaps;
+
+    /* walk up the tree */
+    child = parent;
+    c = p;
+  }
+
+  return swaps;
+}
+
+size_t
+sc_array_pqueue_pop (sc_array_t * array, void *result,
+                     int (*compar) (const void *, const void *))
+{
+  int                 comp;
+  size_t              new_count, swaps;
+  size_t              parent, child, child1;
+  const size_t        size = array->elem_size;
+  void               *p, *c, *c1;
+  void               *temp;
+
+  /* array must not be empty or a view */
+  SC_ASSERT (SC_ARRAY_IS_OWNER (array));
+  SC_ASSERT (array->elem_count > 0);
+
+  /* PQUEUE FUNCTIONS ARE UNTESTED AND CURRENTLY DISABLED. */
+  SC_ABORT_NOT_REACHED ();
+
+  swaps = 0;
+  new_count = array->elem_count - 1;
+
+  /* extract root */
+  parent = 0;
+  p = array->array + (size * parent);
+  memcpy (result, p, size);
+
+  /* copy the last element to the top and reuse it as temp storage */
+  temp = array->array + (size * new_count);
+  if (new_count > 0) {
+    memcpy (p, temp, size);
+  }
+
+  /* sift down the tree */
+  while ((child = 2 * parent + 1) < new_count) {
+    c = array->array + (size * child);
+
+    /* check if child has a sibling and use that one if it is smaller */
+    if ((child1 = 2 * parent + 2) < new_count) {
+      c1 = array->array + (size * child1);
+      comp = compar (c, c1);
+      if (comp > 0) {
+        child = child1;
+        c = c1;
+      }
+    }
+
+    /* sift down the parent if it is larger */
+    comp = compar (p, c);
+    if (comp <= 0) {
+      break;
+    }
+
+    /* swap child and parent */
+    memcpy (temp, c, size);
+    memcpy (c, p, size);
+    memcpy (p, temp, size);
+    ++swaps;
+
+    /* walk down the tree */
+    parent = child;
+    p = c;
+  }
+
+  /* we can resize down here only since we need the temp element above */
+  sc_array_resize (array, new_count);
+
+  return swaps;
+}
+
+/* mempool routines */
+
+size_t
+sc_mempool_memory_used (sc_mempool_t * mempool)
+{
+  return sizeof (sc_mempool_t) +
+    obstack_memory_used (&mempool->obstack) +
+    sc_array_memory_used (&mempool->freed, 0);
+}
+
+static void        *
+sc_containers_malloc (size_t n)
+{
+  return sc_malloc (sc_package_id, n);
+}
+
+static void        *(*obstack_chunk_alloc) (size_t) = sc_containers_malloc;
+
+static void
+sc_containers_free (void *p)
+{
+  sc_free (sc_package_id, p);
+}
+
+static void         (*obstack_chunk_free) (void *) = sc_containers_free;
+
+sc_mempool_t       *
+sc_mempool_new (size_t elem_size)
+{
+  sc_mempool_t       *mempool;
+
+  SC_ASSERT (elem_size > 0);
+  SC_ASSERT (elem_size <= (size_t) INT_MAX);    /* obstack limited to int */
+
+  mempool = SC_ALLOC (sc_mempool_t, 1);
+
+  mempool->elem_size = elem_size;
+  mempool->elem_count = 0;
+
+  obstack_init (&mempool->obstack);
+  sc_array_init (&mempool->freed, sizeof (void *));
+
+  return mempool;
+}
+
+void
+sc_mempool_destroy (sc_mempool_t * mempool)
+{
+  sc_array_reset (&mempool->freed);
+  obstack_free (&mempool->obstack, NULL);
+
+  SC_FREE (mempool);
+}
+
+void
+sc_mempool_truncate (sc_mempool_t * mempool)
+{
+  sc_array_reset (&mempool->freed);
+  obstack_free (&mempool->obstack, NULL);
+  obstack_init (&mempool->obstack);
+  mempool->elem_count = 0;
+}
+
+/* list routines */
+
+size_t
+sc_list_memory_used (sc_list_t * list, int is_dynamic)
+{
+  return (is_dynamic ? sizeof (sc_list_t) : 0) +
+    (list->allocator_owned ? sc_mempool_memory_used (list->allocator) : 0);
+}
+
+sc_list_t          *
+sc_list_new (sc_mempool_t * allocator)
+{
+  sc_list_t          *list;
+
+  list = SC_ALLOC (sc_list_t, 1);
+
+  list->elem_count = 0;
+  list->first = NULL;
+  list->last = NULL;
+
+  if (allocator != NULL) {
+    SC_ASSERT (allocator->elem_size == sizeof (sc_link_t));
+    list->allocator = allocator;
+    list->allocator_owned = 0;
+  }
+  else {
+    list->allocator = sc_mempool_new (sizeof (sc_link_t));
+    list->allocator_owned = 1;
+  }
+
+  return list;
+}
+
+void
+sc_list_destroy (sc_list_t * list)
+{
+
+  if (list->allocator_owned) {
+    sc_list_unlink (list);
+    sc_mempool_destroy (list->allocator);
+  }
+  else {
+    sc_list_reset (list);
+  }
+  SC_FREE (list);
+}
+
+void
+sc_list_init (sc_list_t * list, sc_mempool_t * allocator)
+{
+  list->elem_count = 0;
+  list->first = NULL;
+  list->last = NULL;
+
+  SC_ASSERT (allocator != NULL);
+  SC_ASSERT (allocator->elem_size == sizeof (sc_link_t));
+
+  list->allocator = allocator;
+  list->allocator_owned = 0;
+}
+
+void
+sc_list_reset (sc_list_t * list)
+{
+  sc_link_t          *lynk;
+  sc_link_t          *temp;
+
+  lynk = list->first;
+  while (lynk != NULL) {
+    temp = lynk->next;
+    sc_mempool_free (list->allocator, lynk);
+    lynk = temp;
+    --list->elem_count;
+  }
+  SC_ASSERT (list->elem_count == 0);
+
+  list->first = list->last = NULL;
+}
+
+void
+sc_list_unlink (sc_list_t * list)
+{
+  list->first = list->last = NULL;
+  list->elem_count = 0;
+}
+
+void
+sc_list_prepend (sc_list_t * list, void *data)
+{
+  sc_link_t          *lynk;
+
+  lynk = (sc_link_t *) sc_mempool_alloc (list->allocator);
+  lynk->data = data;
+  lynk->next = list->first;
+  list->first = lynk;
+  if (list->last == NULL) {
+    list->last = lynk;
+  }
+
+  ++list->elem_count;
+}
+
+void
+sc_list_append (sc_list_t * list, void *data)
+{
+  sc_link_t          *lynk;
+
+  lynk = (sc_link_t *) sc_mempool_alloc (list->allocator);
+  lynk->data = data;
+  lynk->next = NULL;
+  if (list->last != NULL) {
+    list->last->next = lynk;
+  }
+  else {
+    list->first = lynk;
+  }
+  list->last = lynk;
+
+  ++list->elem_count;
+}
+
+void
+sc_list_insert (sc_list_t * list, sc_link_t * pred, void *data)
+{
+  sc_link_t          *lynk;
+
+  SC_ASSERT (pred != NULL);
+
+  lynk = (sc_link_t *) sc_mempool_alloc (list->allocator);
+  lynk->data = data;
+  lynk->next = pred->next;
+  pred->next = lynk;
+  if (pred == list->last) {
+    list->last = lynk;
+  }
+
+  ++list->elem_count;
+}
+
+void               *
+sc_list_remove (sc_list_t * list, sc_link_t * pred)
+{
+  sc_link_t          *lynk;
+  void               *data;
+
+  if (pred == NULL) {
+    return sc_list_pop (list);
+  }
+
+  SC_ASSERT (pred->next != NULL);
+
+  lynk = pred->next;
+  pred->next = lynk->next;
+  data = lynk->data;
+  if (list->last == lynk) {
+    list->last = pred;
+  }
+  sc_mempool_free (list->allocator, lynk);
+
+  --list->elem_count;
+  return data;
+}
+
+void               *
+sc_list_pop (sc_list_t * list)
+{
+  sc_link_t          *lynk;
+  void               *data;
+
+  SC_ASSERT (list->first != NULL);
+
+  lynk = list->first;
+  list->first = lynk->next;
+  data = lynk->data;
+  sc_mempool_free (list->allocator, lynk);
+  if (list->first == NULL) {
+    list->last = NULL;
+  }
+
+  --list->elem_count;
+  return data;
+}
+
+/* hash table routines */
+
+unsigned
+sc_hash_function_string (const void *s, const void *u)
+{
+  int                 j;
+  unsigned            h;
+  unsigned            a, b, c;
+  const char         *sp = (const char *) s;
+
+  j = 0;
+  h = 0;
+  a = b = c = 0;
+  for (;;) {
+    if (*sp) {
+      h += *sp++;
+    }
+
+    if (++j == 4) {
+      a += h;
+      h = 0;
+    }
+    else if (j == 8) {
+      b += h;
+      h = 0;
+    }
+    else if (j == 12) {
+      c += h;
+      sc_hash_mix (a, b, c);
+      if (!*sp) {
+        sc_hash_final (a, b, c);
+        return c;
+      }
+      j = 0;
+      h = 0;
+    }
+    else {
+      h <<= 8;
+    }
+  }
+}
+
+size_t
+sc_hash_memory_used (sc_hash_t * hash)
+{
+  return sizeof (sc_hash_t) +
+    sc_array_memory_used (hash->slots, 1) +
+    (hash->allocator_owned ? sc_mempool_memory_used (hash->allocator) : 0);
+}
+
+static const size_t sc_hash_minimal_size = (size_t) ((1 << 8) - 1);
+static const size_t sc_hash_shrink_interval = (size_t) (1 << 8);
+
+static void
+sc_hash_maybe_resize (sc_hash_t * hash)
+{
+  size_t              i, j;
+  size_t              new_size, new_count;
+  sc_list_t          *old_list, *new_list;
+  sc_link_t          *lynk, *temp;
+  sc_array_t         *new_slots;
+  sc_array_t         *old_slots = hash->slots;
+
+  SC_ASSERT (old_slots->elem_count > 0);
+
+  ++hash->resize_checks;
+  if (hash->elem_count >= 4 * old_slots->elem_count) {
+    new_size = 4 * old_slots->elem_count - 1;
+  }
+  else if (hash->elem_count <= old_slots->elem_count / 4) {
+    new_size = old_slots->elem_count / 4 + 1;
+    if (new_size < sc_hash_minimal_size) {
+      return;
+    }
+  }
+  else {
+    return;
+  }
+  ++hash->resize_actions;
+
+  /* allocate new slot array */
+  new_slots = sc_array_new (sizeof (sc_list_t));
+  sc_array_resize (new_slots, new_size);
+  for (i = 0; i < new_size; ++i) {
+    new_list = (sc_list_t *) sc_array_index (new_slots, i);
+    sc_list_init (new_list, hash->allocator);
+  }
+
+  /* go through the old slots and move data to the new slots */
+  new_count = 0;
+  for (i = 0; i < old_slots->elem_count; ++i) {
+    old_list = (sc_list_t *) sc_array_index (old_slots, i);
+    lynk = old_list->first;
+    while (lynk != NULL) {
+      /* insert data into new slot list */
+      j = hash->hash_fn (lynk->data, hash->user_data) % new_size;
+      new_list = (sc_list_t *) sc_array_index (new_slots, j);
+      sc_list_prepend (new_list, lynk->data);
+      ++new_count;
+
+      /* remove old list element */
+      temp = lynk->next;
+      sc_mempool_free (old_list->allocator, lynk);
+      lynk = temp;
+      --old_list->elem_count;
+    }
+    SC_ASSERT (old_list->elem_count == 0);
+    old_list->first = old_list->last = NULL;
+  }
+  SC_ASSERT (new_count == hash->elem_count);
+
+  /* replace old slots by new slots */
+  sc_array_destroy (old_slots);
+  hash->slots = new_slots;
+}
+
+sc_hash_t          *
+sc_hash_new (sc_hash_function_t hash_fn, sc_equal_function_t equal_fn,
+             void *user_data, sc_mempool_t * allocator)
+{
+  size_t              i;
+  sc_hash_t          *hash;
+  sc_list_t          *list;
+  sc_array_t         *slots;
+
+  hash = SC_ALLOC (sc_hash_t, 1);
+
+  if (allocator != NULL) {
+    SC_ASSERT (allocator->elem_size == sizeof (sc_link_t));
+    hash->allocator = allocator;
+    hash->allocator_owned = 0;
+  }
+  else {
+    hash->allocator = sc_mempool_new (sizeof (sc_link_t));
+    hash->allocator_owned = 1;
+  }
+
+  hash->elem_count = 0;
+  hash->resize_checks = 0;
+  hash->resize_actions = 0;
+  hash->hash_fn = hash_fn;
+  hash->equal_fn = equal_fn;
+  hash->user_data = user_data;
+
+  hash->slots = slots = sc_array_new (sizeof (sc_list_t));
+  sc_array_resize (slots, sc_hash_minimal_size);
+  for (i = 0; i < slots->elem_count; ++i) {
+    list = (sc_list_t *) sc_array_index (slots, i);
+    sc_list_init (list, hash->allocator);
+  }
+
+  return hash;
+}
+
+void
+sc_hash_destroy (sc_hash_t * hash)
+{
+  if (hash->allocator_owned) {
+    /* in this case we don't need to clean up each list separately: O(1) */
+    sc_mempool_destroy (hash->allocator);
+  }
+  else {
+    /* return all list elements to the allocator: requires O(N) */
+    sc_hash_truncate (hash);
+  }
+  sc_array_destroy (hash->slots);
+
+  SC_FREE (hash);
+}
+
+void
+sc_hash_truncate (sc_hash_t * hash)
+{
+  size_t              i;
+  size_t              count;
+  sc_list_t          *list;
+  sc_array_t         *slots = hash->slots;
+
+  if (hash->elem_count == 0) {
+    return;
+  }
+
+  if (hash->allocator_owned) {
+    sc_hash_unlink (hash);
+    sc_mempool_truncate (hash->allocator);
+    return;
+  }
+
+  /* return all list elements to the outside memory allocator */
+  for (i = 0, count = 0; i < slots->elem_count; ++i) {
+    list = (sc_list_t *) sc_array_index (slots, i);
+    count += list->elem_count;
+    sc_list_reset (list);
+  }
+  SC_ASSERT (count == hash->elem_count);
+
+  hash->elem_count = 0;
+}
+
+void
+sc_hash_unlink (sc_hash_t * hash)
+{
+  size_t              i, count;
+  sc_list_t          *list;
+  sc_array_t         *slots = hash->slots;
+
+  for (i = 0, count = 0; i < slots->elem_count; ++i) {
+    list = (sc_list_t *) sc_array_index (slots, i);
+    count += list->elem_count;
+    sc_list_unlink (list);
+  }
+  SC_ASSERT (count == hash->elem_count);
+
+  hash->elem_count = 0;
+}
+
+void
+sc_hash_unlink_destroy (sc_hash_t * hash)
+{
+  if (hash->allocator_owned) {
+    sc_mempool_destroy (hash->allocator);
+  }
+  sc_array_destroy (hash->slots);
+
+  SC_FREE (hash);
+}
+
+int
+sc_hash_lookup (sc_hash_t * hash, void *v, void ***found)
+{
+  size_t              hval;
+  sc_list_t          *list;
+  sc_link_t          *lynk;
+
+  hval = hash->hash_fn (v, hash->user_data) % hash->slots->elem_count;
+  list = (sc_list_t *) sc_array_index (hash->slots, hval);
+
+  for (lynk = list->first; lynk != NULL; lynk = lynk->next) {
+    /* check if an equal object is contained in the hash table */
+    if (hash->equal_fn (lynk->data, v, hash->user_data)) {
+      if (found != NULL) {
+        *found = &lynk->data;
+      }
+      return 1;
+    }
+  }
+  return 0;
+}
+
+int
+sc_hash_insert_unique (sc_hash_t * hash, void *v, void ***found)
+{
+  size_t              hval;
+  sc_list_t          *list;
+  sc_link_t          *lynk;
+
+  hval = hash->hash_fn (v, hash->user_data) % hash->slots->elem_count;
+  list = (sc_list_t *) sc_array_index (hash->slots, hval);
+
+  /* check if an equal object is already contained in the hash table */
+  for (lynk = list->first; lynk != NULL; lynk = lynk->next) {
+    if (hash->equal_fn (lynk->data, v, hash->user_data)) {
+      if (found != NULL) {
+        *found = &lynk->data;
+      }
+      return 0;
+    }
+  }
+
+  /* append new object to the list */
+  sc_list_append (list, v);
+  if (found != NULL) {
+    *found = &list->last->data;
+  }
+  ++hash->elem_count;
+
+  /* check for resize at specific intervals and reassign output */
+  if (hash->elem_count % hash->slots->elem_count == 0) {
+    sc_hash_maybe_resize (hash);
+    if (found != NULL) {
+      SC_EXECUTE_ASSERT_TRUE (sc_hash_lookup (hash, v, found));
+    }
+  }
+
+  return 1;
+}
+
+int
+sc_hash_remove (sc_hash_t * hash, void *v, void **found)
+{
+  size_t              hval;
+  sc_list_t          *list;
+  sc_link_t          *lynk, *prev;
+
+  hval = hash->hash_fn (v, hash->user_data) % hash->slots->elem_count;
+  list = (sc_list_t *) sc_array_index (hash->slots, hval);
+
+  prev = NULL;
+  for (lynk = list->first; lynk != NULL; lynk = lynk->next) {
+    /* check if an equal object is contained in the hash table */
+    if (hash->equal_fn (lynk->data, v, hash->user_data)) {
+      if (found != NULL) {
+        *found = lynk->data;
+      }
+      (void) sc_list_remove (list, prev);
+      --hash->elem_count;
+
+      /* check for resize at specific intervals and return */
+      if (hash->elem_count % sc_hash_shrink_interval == 0) {
+        sc_hash_maybe_resize (hash);
+      }
+      return 1;
+    }
+    prev = lynk;
+  }
+  return 0;
+}
+
+void
+sc_hash_foreach (sc_hash_t * hash, sc_hash_foreach_t fn)
+{
+  size_t              slot;
+  sc_list_t          *list;
+  sc_link_t          *lynk;
+
+  for (slot = 0; slot < hash->slots->elem_count; ++slot) {
+    list = (sc_list_t *) sc_array_index (hash->slots, slot);
+    for (lynk = list->first; lynk != NULL; lynk = lynk->next) {
+      if (!fn (&lynk->data, hash->user_data)) {
+        return;
+      }
+    }
+  }
+}
+
+void
+sc_hash_print_statistics (int package_id, int log_priority, sc_hash_t * hash)
+{
+  size_t              i;
+  double              a, sum, squaresum;
+  double              divide, avg, sqr, std;
+  sc_list_t          *list;
+  sc_array_t         *slots = hash->slots;
+
+  sum = 0.;
+  squaresum = 0.;
+  for (i = 0; i < slots->elem_count; ++i) {
+    list = (sc_list_t *) sc_array_index (slots, i);
+    a = (double) list->elem_count;
+    sum += a;
+    squaresum += a * a;
+  }
+  SC_ASSERT ((size_t) sum == hash->elem_count);
+
+  divide = (double) slots->elem_count;
+  avg = sum / divide;
+  sqr = squaresum / divide - avg * avg;
+  std = sqrt (sqr);
+  SC_GEN_LOGF (package_id, SC_LC_NORMAL, log_priority,
+               "Hash size %lu avg %.3g std %.3g checks %lu %lu\n",
+               (unsigned long) slots->elem_count, avg, std,
+               (unsigned long) hash->resize_checks,
+               (unsigned long) hash->resize_actions);
+}
+
+/* hash array routines */
+
+size_t
+sc_hash_array_memory_used (sc_hash_array_t * ha)
+{
+  return sizeof (sc_hash_array_t) +
+    sc_array_memory_used (&ha->a, 0) + sc_hash_memory_used (ha->h);
+}
+
+static unsigned
+sc_hash_array_hash_fn (const void *v, const void *u)
+{
+  const sc_hash_array_data_t *internal_data =
+    (const sc_hash_array_data_t *) u;
+  long                l = (long) v;
+  void               *p;
+
+  p = (l == -1L) ? internal_data->current_item :
+    sc_array_index_long (internal_data->pa, l);
+
+  return internal_data->hash_fn (p, internal_data->user_data);
+}
+
+static int
+sc_hash_array_equal_fn (const void *v1, const void *v2, const void *u)
+{
+  const sc_hash_array_data_t *internal_data =
+    (const sc_hash_array_data_t *) u;
+  long                l1 = (long) v1;
+  long                l2 = (long) v2;
+  void               *p1, *p2;
+
+  p1 = (l1 == -1L) ? internal_data->current_item :
+    sc_array_index_long (internal_data->pa, l1);
+  p2 = (l2 == -1L) ? internal_data->current_item :
+    sc_array_index_long (internal_data->pa, l2);
+
+  return internal_data->equal_fn (p1, p2, internal_data->user_data);
+}
+
+sc_hash_array_t    *
+sc_hash_array_new (size_t elem_size, sc_hash_function_t hash_fn,
+                   sc_equal_function_t equal_fn, void *user_data)
+{
+  sc_hash_array_t    *hash_array;
+
+  hash_array = SC_ALLOC (sc_hash_array_t, 1);
+
+  sc_array_init (&hash_array->a, elem_size);
+  hash_array->internal_data.pa = &hash_array->a;
+  hash_array->internal_data.hash_fn = hash_fn;
+  hash_array->internal_data.equal_fn = equal_fn;
+  hash_array->internal_data.user_data = user_data;
+  hash_array->internal_data.current_item = NULL;
+  hash_array->h = sc_hash_new (sc_hash_array_hash_fn, sc_hash_array_equal_fn,
+                               &hash_array->internal_data, NULL);
+
+  return hash_array;
+}
+
+void
+sc_hash_array_destroy (sc_hash_array_t * hash_array)
+{
+  sc_hash_destroy (hash_array->h);
+  sc_array_reset (&hash_array->a);
+
+  SC_FREE (hash_array);
+}
+
+int
+sc_hash_array_is_valid (sc_hash_array_t * hash_array)
+{
+  int                 found;
+  size_t              zz, position;
+  void               *v;
+
+  for (zz = 0; zz < hash_array->a.elem_count; ++zz) {
+    v = sc_array_index (&hash_array->a, zz);
+    found = sc_hash_array_lookup (hash_array, v, &position);
+    if (!found || position != zz) {
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+void
+sc_hash_array_truncate (sc_hash_array_t * hash_array)
+{
+  sc_hash_truncate (hash_array->h);
+  sc_array_reset (&hash_array->a);
+}
+
+int
+sc_hash_array_lookup (sc_hash_array_t * hash_array, void *v,
+                      size_t * position)
+{
+  int                 found;
+  void              **found_void;
+
+  hash_array->internal_data.current_item = v;
+  found = sc_hash_lookup (hash_array->h, (void *) (-1L), &found_void);
+  hash_array->internal_data.current_item = NULL;
+
+  if (found) {
+    if (position != NULL) {
+      *position = (size_t) (*found_void);
+    }
+    return 1;
+  }
+  else {
+    return 0;
+  }
+}
+
+void               *
+sc_hash_array_insert_unique (sc_hash_array_t * hash_array, void *v,
+                             size_t * position)
+{
+  int                 added;
+  void              **found_void;
+
+  SC_ASSERT (hash_array->a.elem_count == hash_array->h->elem_count);
+
+  hash_array->internal_data.current_item = v;
+  added = sc_hash_insert_unique (hash_array->h, (void *) (-1L), &found_void);
+  hash_array->internal_data.current_item = NULL;
+
+  if (added) {
+    if (position != NULL) {
+      *position = hash_array->a.elem_count;
+    }
+    *found_void = (void *) hash_array->a.elem_count;
+    return sc_array_push (&hash_array->a);
+  }
+  else {
+    if (position != NULL) {
+      *position = (size_t) (*found_void);
+    }
+    return NULL;
+  }
+}
+
+void
+sc_hash_array_rip (sc_hash_array_t * hash_array, sc_array_t * rip)
+{
+  sc_hash_destroy (hash_array->h);
+  memcpy (rip, &hash_array->a, sizeof (sc_array_t));
+
+  SC_FREE (hash_array);
+}
+
+void
+sc_recycle_array_init (sc_recycle_array_t * rec_array, size_t elem_size)
+{
+  sc_array_init (&rec_array->a, elem_size);
+  sc_array_init (&rec_array->f, sizeof (size_t));
+
+  rec_array->elem_count = 0;
+}
+
+void
+sc_recycle_array_reset (sc_recycle_array_t * rec_array)
+{
+  SC_ASSERT (rec_array->a.elem_count ==
+             rec_array->elem_count + rec_array->f.elem_count);
+
+  sc_array_reset (&rec_array->a);
+  sc_array_reset (&rec_array->f);
+
+  rec_array->elem_count = 0;
+}
+
+void               *
+sc_recycle_array_insert (sc_recycle_array_t * rec_array, size_t * position)
+{
+  size_t              newpos;
+  void               *newitem;
+
+  if (rec_array->f.elem_count > 0) {
+    newpos = *(size_t *) sc_array_pop (&rec_array->f);
+    newitem = sc_array_index (&rec_array->a, newpos);
+  }
+  else {
+    newpos = rec_array->a.elem_count;
+    newitem = sc_array_push (&rec_array->a);
+  }
+
+  if (position != NULL) {
+    *position = newpos;
+  }
+  ++rec_array->elem_count;
+
+  return newitem;
+}
+
+void               *
+sc_recycle_array_remove (sc_recycle_array_t * rec_array, size_t position)
+{
+  SC_ASSERT (rec_array->elem_count > 0);
+
+  *(size_t *) sc_array_push (&rec_array->f) = position;
+  --rec_array->elem_count;
+
+  return sc_array_index (&rec_array->a, position);
+}
diff --git a/sc/src/sc_containers.h b/sc/src/sc_containers.h
new file mode 100644
index 0000000..30eab7c
--- /dev/null
+++ b/sc/src/sc_containers.h
@@ -0,0 +1,905 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_CONTAINERS_H
+#define SC_CONTAINERS_H
+
+/** \file sc_containers.h
+ *
+ * Defines lists, arrays, hash tables, etc.
+ *
+ * \ingroup containers
+ */
+
+/** \defgroup containers containers
+ *
+ * Defines lists, arrays, hash tables, etc.
+ *
+ * \ingroup sc
+ */
+
+#include <sc_obstack.h>
+
+SC_EXTERN_C_BEGIN;
+
+/* Hash macros from lookup3.c by Bob Jenkins, May 2006, public domain. */
+#define sc_hash_rot(x,k) (((x) << (k)) | ((x) >> (32 - (k))))
+#define sc_hash_mix(a,b,c) ((void)                                      \
+                            (a -= c, a ^= sc_hash_rot(c, 4), c += b,    \
+                             b -= a, b ^= sc_hash_rot(a, 6), a += c,    \
+                             c -= b, c ^= sc_hash_rot(b, 8), b += a,    \
+                             a -= c, a ^= sc_hash_rot(c,16), c += b,    \
+                             b -= a, b ^= sc_hash_rot(a,19), a += c,    \
+                             c -= b, c ^= sc_hash_rot(b, 4), b += a))
+#define sc_hash_final(a,b,c) ((void)                            \
+                              (c ^= b, c -= sc_hash_rot(b,14),  \
+                               a ^= c, a -= sc_hash_rot(c,11),  \
+                               b ^= a, b -= sc_hash_rot(a,25),  \
+                               c ^= b, c -= sc_hash_rot(b,16),  \
+                               a ^= c, a -= sc_hash_rot(c, 4),  \
+                               b ^= a, b -= sc_hash_rot(a,14),  \
+                               c ^= b, c -= sc_hash_rot(b,24)))
+
+/** Function to compute a hash value of an object.
+ * \param [in] v   The object to hash.
+ * \param [in] u   Arbitrary user data.
+ * \return Returns an unsigned integer.
+ */
+typedef unsigned    (*sc_hash_function_t) (const void *v, const void *u);
+
+/** Function to check equality of two objects.
+ * \param [in] u   Arbitrary user data.
+ * \return Returns false if *v1 is unequal *v2 and true otherwise.
+ */
+typedef int         (*sc_equal_function_t) (const void *v1,
+                                            const void *v2, const void *u);
+
+/** Function to call on every data item of a hash table.
+ * \param [in] v   The address of the pointer to the current object.
+ * \param [in] u   Arbitrary user data.
+ * \return Return true if the traversal should continue, false to stop.
+ */
+typedef int         (*sc_hash_foreach_t) (void **v, const void *u);
+
+/** The sc_array object provides a large array of equal-size elements.
+ * The array can be resized.
+ * Elements are accessed by their 0-based index, their address may change.
+ * The size (== elem_count) of the array can be changed by array_resize.
+ * Elements can be sorted with array_sort.
+ * If the array is sorted elements can be binary searched with array_bsearch.
+ * A priority queue is implemented with pqueue_add and pqueue_pop.
+ * Use sort and search whenever possible, they are faster than the pqueue.
+ */
+typedef struct sc_array
+{
+  /* interface variables */
+  size_t              elem_size;        /**< size of a single element */
+  size_t              elem_count;       /**< number of valid elements */
+
+  /* implementation variables */
+  ssize_t             byte_alloc;       /**< number of allocated bytes
+                                           or -(number of viewed bytes + 1)
+                                           if this is a view: the "+ 1"
+                                           distinguishes an array of size 0
+                                           from a view of size 0 */
+  char               *array;    /**< linear array to store elements */
+}
+sc_array_t;
+
+/** test whether the sc_array_t owns its \a array */
+#define SC_ARRAY_IS_OWNER(a) ((a)->byte_alloc >= 0)
+/** the allocated size of the array */
+#define SC_ARRAY_BYTE_ALLOC(a) ((size_t) \
+         (SC_ARRAY_IS_OWNER (a) ? (a)->byte_alloc : -((a)->byte_alloc + 1)))
+
+/** Calculate the memory used by an array.
+ * \param [in] array       The array.
+ * \param [in] is_dynamic  True if created with sc_array_new,
+ *                         false if initialized with sc_array_init
+ * \return                 Memory used in bytes.
+ */
+size_t              sc_array_memory_used (sc_array_t * array, int is_dynamic);
+
+/** Creates a new array structure with 0 elements.
+ * \param [in] elem_size    Size of one array element in bytes.
+ * \return                  Return an allocated array of zero length.
+ */
+sc_array_t         *sc_array_new (size_t elem_size);
+
+/** Creates a new array structure with a given length (number of elements).
+ * \param [in] elem_size    Size of one array element in bytes.
+ * \param [in] elem_count   Initial number of array elements.
+ * \return                  Return an allocated array
+ *                          with allocated but uninitialized elements.
+ */
+sc_array_t         *sc_array_new_size (size_t elem_size, size_t elem_count);
+
+/** Creates a new view of an existing sc_array_t.
+ * \param [in] array    The array must not be resized while view is alive.
+ * \param [in] offset   The offset of the viewed section in element units.
+ *                      This offset cannot be changed until the view is reset.
+ * \param [in] length   The length of the viewed section in element units.
+ *                      The view cannot be resized to exceed this length.
+ */
+sc_array_t         *sc_array_new_view (sc_array_t * array,
+                                       size_t offset, size_t length);
+
+/** Creates a new view of an existing plain C array.
+ * \param [in] base         The data must not be moved while view is alive.
+ * \param [in] elem_size    Size of one array element in bytes.
+ * \param [in] elem_count   The length of the view in element units.
+ *                          The view cannot be resized to exceed this length.
+ */
+sc_array_t         *sc_array_new_data (void *base,
+                                       size_t elem_size, size_t elem_count);
+
+/** Destroys an array structure.
+ * \param [in] array    The array to be destroyed.
+ */
+void                sc_array_destroy (sc_array_t * array);
+
+/** Initializes an already allocated (or static) array structure.
+ * \param [in,out]  array       Array structure to be initialized.
+ * \param [in] elem_size        Size of one array element in bytes.
+ */
+void                sc_array_init (sc_array_t * array, size_t elem_size);
+
+/** Initializes an already allocated (or static) array structure
+ * and allocates a given number of elements.
+ * \param [in,out]  array       Array structure to be initialized.
+ * \param [in] elem_size        Size of one array element in bytes.
+ * \param [in] elem_count       Number of initial array elements.
+ */
+void                sc_array_init_size (sc_array_t * array,
+                                        size_t elem_size, size_t elem_count);
+
+/** Initializes an already allocated (or static) view from existing sc_array_t.
+ * \param [in,out] view  Array structure to be initialized.
+ * \param [in] array     The array must not be resized while view is alive.
+ * \param [in] offset    The offset of the viewed section in element units.
+ *                       This offset cannot be changed until the view is reset.
+ * \param [in] length    The length of the view in element units.
+ *                       The view cannot be resized to exceed this length.
+ */
+void                sc_array_init_view (sc_array_t * view, sc_array_t * array,
+                                        size_t offset, size_t length);
+
+/** Initializes an already allocated (or static) view from given plain C data.
+ * \param [in,out] view     Array structure to be initialized.
+ * \param [in] base         The data must not be moved while view is alive.
+ * \param [in] elem_size    Size of one array element in bytes.
+ * \param [in] elem_count   The length of the view in element units.
+ *                          The view cannot be resized to exceed this length.
+ */
+void                sc_array_init_data (sc_array_t * view, void *base,
+                                        size_t elem_size, size_t elem_count);
+
+/** Sets the array count to zero and frees all elements.
+ * This function turns a view into a newly initialized array.
+ * \param [in,out]  array       Array structure to be reset.
+ * \note Calling sc_array_init, then any array operations,
+ *       then sc_array_reset is memory neutral.
+ */
+void                sc_array_reset (sc_array_t * array);
+
+/** Sets the array count to zero, but does not free elements.
+ * Not allowed for views.
+ * \param [in,out]  array       Array structure to be truncated.
+ * \note This is intended to allow an sc_array to be used as a reusable
+ * buffer, where the "high water mark" of the buffer is preserved, so that
+ * O(log (max n)) reallocs occur over the life of the buffer.
+ */
+void                sc_array_truncate (sc_array_t * array);
+
+/** Sets the element count to new_count.
+ * If this a view, new_count cannot be greater than the elem_count of
+ * the view when it was created.  The original offset of the view cannot be
+ * changed.
+ * If this is an array, reallocation takes place only occasionally, so
+ * this function is usually fast.
+ */
+void                sc_array_resize (sc_array_t * array, size_t new_count);
+
+/** Copy the contents of an array into another.
+ * Both arrays must have equal element sizes.
+ * \param [in] dest Array (not a view) will be resized and get new data.
+ * \param [in] src  Array used as source of new data, will not be changed.
+ */
+void                sc_array_copy (sc_array_t * dest, sc_array_t * src);
+
+/** Sorts the array in ascending order wrt. the comparison function.
+ * \param [in] array    The array to sort.
+ * \param [in] compar   The comparison function to be used.
+ */
+void                sc_array_sort (sc_array_t * array,
+                                   int (*compar) (const void *,
+                                                  const void *));
+
+/** Check whether the array is sorted wrt. the comparison function.
+ * \param [in] array    The array to check.
+ * \param [in] compar   The comparison function to be used.
+ * \return              True if array is sorted, false otherwise.
+ */
+int                 sc_array_is_sorted (sc_array_t * array,
+                                        int (*compar) (const void *,
+                                                       const void *));
+
+/** Check whether two arrays have equal size, count, and content.
+ * Either array may be a view.  Both arrays will not be changed.
+ * \param [in] array   One array to be compared.
+ * \param [in] other   A second array to be compared.
+ * \return              True if array and other are equal, false otherwise.
+ */
+int                 sc_array_is_equal (sc_array_t * array,
+                                       sc_array_t * other);
+
+/** Removed duplicate entries from a sorted array.
+ * This function is not allowed for views.
+ * \param [in,out] array  The array size will be reduced as necessary.
+ * \param [in] compar     The comparison function to be used.
+ */
+void                sc_array_uniq (sc_array_t * array,
+                                   int (*compar) (const void *,
+                                                  const void *));
+
+/** Performs a binary search on an array. The array must be sorted.
+ * \param [in] array   A sorted array to search in.
+ * \param [in] key     An element to be searched for.
+ * \param [in] compar  The comparison function to be used.
+ * \return Returns the index into array for the item found, or -1.
+ */
+ssize_t             sc_array_bsearch (sc_array_t * array,
+                                      const void *key,
+                                      int (*compar) (const void *,
+                                                     const void *));
+
+/** Function to determine the enumerable type of an object in an array.
+ * \param [in] array   Array containing the object.
+ * \param [in] index   The location of the object.
+ * \param [in] data    Arbitrary user data.
+ */
+typedef             size_t (*sc_array_type_t) (sc_array_t * array,
+                                               size_t index, void *data);
+
+/** Compute the offsets of groups of enumerable types in an array.
+ * \param [in] array         Array that is sorted in ascending order by type.
+ *                           If k indexes \a array, then
+ *                           0 <= \a type_fn (\a array, k, \a data) <
+ *                           \a num_types.
+ * \param [in,out] offsets   An initialized array of type size_t that is
+ *                           resized to \a num_types + 1 entries.  The indices
+ *                           j of \a array that contain objects of type k are
+ *                           \a offsets[k] <= j < \a offsets[k + 1].
+ *                           If there are no objects of type k, then
+ *                           \a offsets[k] = \a offset[k + 1].
+ * \param [in] num_types     The number of possible types of objects in
+ *                           \a array.
+ * \param [in] type_fn       Returns the type of an object in the array.
+ * \param [in] data          Arbitrary user data passed to \a type_fn.
+ */
+void                sc_array_split (sc_array_t * array, sc_array_t * offsets,
+                                    size_t num_types, sc_array_type_t type_fn,
+                                    void *data);
+
+/** Determine whether \a array is an array of size_t's whose entries include
+ * every integer 0 <= i < array->elem_count.
+ * \param [in] array         An array.
+ * \return                   Returns 1 if array contains size_t elements whose
+ *                           entries include every integer
+ *                           0 <= i < \a array->elem_count, 0 otherwise.
+ */
+int                 sc_array_is_permutation (sc_array_t * array);
+
+/** Given permutation \a newindices, permute \a array in place.  The data that
+ * on input is contained in \a array[i] will be contained in \a
+ * array[newindices[i]] on output.  The entries of newindices will be altered
+ * unless \a keepperm is true.
+ * \param [in,out] array      An array.
+ * \param [in,out] newindices Permutation array (see sc_array_is_permutation).
+ * \param [in]     keepperm   If true, \a newindices will be unchanged by the
+ *                            algorithm; if false, \a newindices will be the
+ *                            identity permutation on output, but the
+ *                            algorithm will only use O(1) space.
+ */
+void                sc_array_permute (sc_array_t * array,
+                                      sc_array_t * newindices, int keepperm);
+
+/** Computes the adler32 checksum of array data (see zlib documentation).
+ * This is a faster checksum than crc32, and it works with zeros as data.
+ */
+unsigned            sc_array_checksum (sc_array_t * array);
+
+/** Adds an element to a priority queue.
+ * PQUEUE FUNCTIONS ARE UNTESTED AND CURRENTLY DISABLED.
+ * This function is not allowed for views.
+ * The priority queue is implemented as a heap in ascending order.
+ * A heap is a binary tree where the children are not less than their parent.
+ * Assumes that elements [0]..[elem_count-2] form a valid heap.
+ * Then propagates [elem_count-1] upward by swapping if necessary.
+ * \param [in] temp    Pointer to unused allocated memory of elem_size.
+ * \param [in] compar  The comparison function to be used.
+ * \return Returns the number of swap operations.
+ * \note  If the return value is zero for all elements in an array,
+ *        the array is sorted linearly and unchanged.
+ */
+size_t              sc_array_pqueue_add (sc_array_t * array,
+                                         void *temp,
+                                         int (*compar) (const void *,
+                                                        const void *));
+
+/** Pops the smallest element from a priority queue.
+ * PQUEUE FUNCTIONS ARE UNTESTED AND CURRENTLY DISABLED.
+ * This function is not allowed for views.
+ * This function assumes that the array forms a valid heap in ascending order.
+ * \param [out] result  Pointer to unused allocated memory of elem_size.
+ * \param [in]  compar  The comparison function to be used.
+ * \return Returns the number of swap operations.
+ * \note This function resizes the array to elem_count-1.
+ */
+size_t              sc_array_pqueue_pop (sc_array_t * array,
+                                         void *result,
+                                         int (*compar) (const void *,
+                                                        const void *));
+
+/** Returns a pointer to an array element.
+ * \param [in] index needs to be in [0]..[elem_count-1].
+ */
+/*@unused@*/
+static inline void *
+sc_array_index (sc_array_t * array, size_t iz)
+{
+  SC_ASSERT (iz < array->elem_count);
+
+  return (void *) (array->array + (array->elem_size * iz));
+}
+
+/** Returns a pointer to an array element indexed by a plain int.
+ * \param [in] index needs to be in [0]..[elem_count-1].
+ */
+/*@unused@*/
+static inline void *
+sc_array_index_int (sc_array_t * array, int i)
+{
+  SC_ASSERT (i >= 0 && (size_t) i < array->elem_count);
+
+  return (void *) (array->array + (array->elem_size * (size_t) i));
+}
+
+/** Returns a pointer to an array element indexed by a plain long.
+ * \param [in] index needs to be in [0]..[elem_count-1].
+ */
+/*@unused@*/
+static inline void *
+sc_array_index_long (sc_array_t * array, long l)
+{
+  SC_ASSERT (l >= 0 && (size_t) l < array->elem_count);
+
+  return (void *) (array->array + (array->elem_size * (size_t) l));
+}
+
+/** Returns a pointer to an array element indexed by a ssize_t.
+ * \param [in] index needs to be in [0]..[elem_count-1].
+ */
+/*@unused@*/
+static inline void *
+sc_array_index_ssize_t (sc_array_t * array, ssize_t is)
+{
+  SC_ASSERT (is >= 0 && (size_t) is < array->elem_count);
+
+  return (void *) (array->array + (array->elem_size * (size_t) is));
+}
+
+/** Returns a pointer to an array element indexed by a int16_t.
+ * \param [in] index needs to be in [0]..[elem_count-1].
+ */
+/*@unused@*/
+static inline void *
+sc_array_index_int16 (sc_array_t * array, int16_t i16)
+{
+  SC_ASSERT (i16 >= 0 && (size_t) i16 < array->elem_count);
+
+  return (void *) (array->array + (array->elem_size * (size_t) i16));
+}
+
+/** Return the index of an object in an array identified by a pointer.
+ * \param [in] element needs to be the address of an element in array.
+ */
+/*@unused@*/
+static inline       size_t
+sc_array_position (sc_array_t * array, void *element)
+{
+  size_t              position;
+
+  SC_ASSERT (array->array <= (char *) element);
+  SC_ASSERT (((char *) element - array->array) % array->elem_size == 0);
+
+  position = ((char *) element - array->array) / array->elem_size;
+  SC_ASSERT (position < array->elem_count);
+
+  return position;
+}
+
+/** Remove the last element from an array and return a pointer to it.
+ * This function is not allowed for views.
+ * \return                The pointer to the removed object.  Will be valid
+ *                        as long as no other function is called on this array.
+ */
+/*@unused@*/
+static inline void *
+sc_array_pop (sc_array_t * array)
+{
+  SC_ASSERT (SC_ARRAY_IS_OWNER (array));
+  SC_ASSERT (array->elem_count > 0);
+
+  return (void *) (array->array + (array->elem_size * --array->elem_count));
+}
+
+/** Enlarge an array by a number of elements.  Grows the array if necessary.
+ * This function is not allowed for views.
+ * \return Returns a pointer to the uninitialized newly added elements.
+ */
+/*@unused@*/
+static inline void *
+sc_array_push_count (sc_array_t * array, size_t add_count)
+{
+  const size_t        old_count = array->elem_count;
+  const size_t        new_count = old_count + add_count;
+
+  SC_ASSERT (SC_ARRAY_IS_OWNER (array));
+
+  if (array->elem_size * new_count > (size_t) array->byte_alloc) {
+    sc_array_resize (array, new_count);
+  }
+  else {
+    array->elem_count = new_count;
+  }
+
+  return (void *) (array->array + array->elem_size * old_count);
+}
+
+/** Enlarge an array by one element.  Grows the array if necessary.
+ * This function is not allowed for views.
+ * \return Returns a pointer to the uninitialized newly added element.
+ */
+/*@unused@*/
+static inline void *
+sc_array_push (sc_array_t * array)
+{
+  return sc_array_push_count (array, 1);
+}
+
+/** The sc_mempool object provides a large pool of equal-size elements.
+ * The pool grows dynamically for element allocation.
+ * Elements are referenced by their address which never changes.
+ * Elements can be freed (that is, returned to the pool)
+ *    and are transparently reused.
+ */
+typedef struct sc_mempool
+{
+  /* interface variables */
+  size_t              elem_size;        /**< size of a single element */
+  size_t              elem_count;       /**< number of valid elements */
+
+  /* implementation variables */
+  struct obstack      obstack;  /**< holds the allocated elements */
+  sc_array_t          freed;    /**< buffers the freed elements */
+}
+sc_mempool_t;
+
+/** Calculate the memory used by a memory pool.
+ * \param [in] array       The memory pool.
+ * \return                 Memory used in bytes.
+ */
+size_t              sc_mempool_memory_used (sc_mempool_t * mempool);
+
+/** Creates a new mempool structure.
+ * \param [in] elem_size  Size of one element in bytes.
+ * \return Returns an allocated and initialized memory pool.
+ */
+sc_mempool_t       *sc_mempool_new (size_t elem_size);
+
+/** Destroys a mempool structure.
+ * All elements that are still in use are invalidated.
+ */
+void                sc_mempool_destroy (sc_mempool_t * mempool);
+
+/** Invalidates all previously returned pointers, resets count to 0.
+ */
+void                sc_mempool_truncate (sc_mempool_t * mempool);
+
+/** Allocate a single element.
+ * Elements previously returned to the pool are recycled.
+ * \return Returns a new or recycled element pointer.
+ */
+/*@unused@*/
+static inline void *
+sc_mempool_alloc (sc_mempool_t * mempool)
+{
+  void               *ret;
+  sc_array_t         *freed = &mempool->freed;
+
+  ++mempool->elem_count;
+
+  if (freed->elem_count > 0) {
+    ret = *(void **) sc_array_pop (freed);
+  }
+  else {
+    ret = obstack_alloc (&mempool->obstack, (int) mempool->elem_size);
+  }
+
+#ifdef SC_DEBUG
+  memset (ret, -1, mempool->elem_size);
+#endif
+
+  return ret;
+}
+
+/** Return a previously allocated element to the pool.
+ * \param [in] elem  The element to be returned to the pool.
+ */
+/*@unused@*/
+static inline void
+sc_mempool_free (sc_mempool_t * mempool, void *elem)
+{
+  sc_array_t         *freed = &mempool->freed;
+
+  SC_ASSERT (mempool->elem_count > 0);
+
+#ifdef SC_DEBUG
+  memset (elem, -1, mempool->elem_size);
+#endif
+
+  --mempool->elem_count;
+
+  *(void **) sc_array_push (freed) = elem;
+}
+
+/** The sc_link structure is one link of a linked list.
+ */
+typedef struct sc_link
+{
+  void               *data;
+  struct sc_link     *next;
+}
+sc_link_t;
+
+/** The sc_list object provides a linked list.
+ */
+typedef struct sc_list
+{
+  /* interface variables */
+  size_t              elem_count;
+  sc_link_t          *first;
+  sc_link_t          *last;
+
+  /* implementation variables */
+  int                 allocator_owned;
+  sc_mempool_t       *allocator;        /* must allocate sc_link_t */
+}
+sc_list_t;
+
+/** Calculate the memory used by a list.
+ * \param [in] list        The list.
+ * \param [in] is_dynamic  True if created with sc_list_new,
+ *                         false if initialized with sc_list_init
+ * \return                 Memory used in bytes.
+ */
+size_t              sc_list_memory_used (sc_list_t * list, int is_dynamic);
+
+/** Allocate a linked list structure.
+ * \param [in] allocator Memory allocator for sc_link_t, can be NULL.
+ */
+sc_list_t          *sc_list_new (sc_mempool_t * allocator);
+
+/** Destroy a linked list structure in O(N).
+ * \note If allocator was provided in sc_list_new, it will not be destroyed.
+ */
+void                sc_list_destroy (sc_list_t * list);
+
+/** Initializes an already allocated list structure.
+ * \param [in,out]  list       List structure to be initialized.
+ * \param [in]      allocator  External memory allocator for sc_link_t.
+ */
+void                sc_list_init (sc_list_t * list, sc_mempool_t * allocator);
+
+/** Removes all elements from a list in O(N).
+ * \param [in,out]  list       List structure to be resetted.
+ * \note Calling sc_list_init, then any list operations,
+ *       then sc_list_reset is memory neutral.
+ */
+void                sc_list_reset (sc_list_t * list);
+
+/** Unliks all list elements without returning them to the mempool.
+ * This runs in O(1) but is dangerous because of potential memory leaks.
+ * \param [in,out]  list       List structure to be unlinked.
+ */
+void                sc_list_unlink (sc_list_t * list);
+
+void                sc_list_prepend (sc_list_t * list, void *data);
+void                sc_list_append (sc_list_t * list, void *data);
+
+/** Insert an element after a given position.
+ * \param [in] pred The predecessor of the element to be inserted.
+ */
+void                sc_list_insert (sc_list_t * list,
+                                    sc_link_t * pred, void *data);
+
+/** Remove an element after a given position.
+ * \param [in] pred  The predecessor of the element to be removed.
+                     If \a pred == NULL, the first element is removed.
+ * \return Returns the data of the removed element.
+ */
+void               *sc_list_remove (sc_list_t * list, sc_link_t * pred);
+
+/** Remove an element from the front of the list.
+ * \return Returns the data of the removed first list element.
+ */
+void               *sc_list_pop (sc_list_t * list);
+
+/** The sc_hash implements a hash table.
+ * It uses an array which has linked lists as elements.
+ */
+typedef struct sc_hash
+{
+  /* interface variables */
+  size_t              elem_count;       /**< total number of objects contained */
+
+  /* implementation variables */
+  sc_array_t         *slots;    /**< the slot count is slots->elem_count */
+  void               *user_data;        /**< user data passed to hash function */
+  sc_hash_function_t  hash_fn;
+  sc_equal_function_t equal_fn;
+  size_t              resize_checks, resize_actions;
+  int                 allocator_owned;
+  sc_mempool_t       *allocator;        /**< must allocate sc_link_t */
+}
+sc_hash_t;
+
+/** Compute a hash value from a null-terminated string.
+ * This hash function is NOT cryptographically safe! Use libcrypt then.
+ * \param [in] s        Null-terminated string to be hashed.
+ * \param [in] u        Not used.
+ * \return              The computed hash value as an unsigned integer.
+ */
+unsigned            sc_hash_function_string (const void *s, const void *u);
+
+/** Calculate the memory used by a hash table.
+ * \param [in] hash        The hash table.
+ * \return                 Memory used in bytes.
+ */
+size_t              sc_hash_memory_used (sc_hash_t * hash);
+
+/** Create a new hash table.
+ * The number of hash slots is chosen dynamically.
+ * \param [in] hash_fn     Function to compute the hash value.
+ * \param [in] equal_fn    Function to test two objects for equality.
+ * \param [in] user_data   User data passed through to the hash function.
+ * \param [in] allocator   Memory allocator for sc_link_t, can be NULL.
+ */
+sc_hash_t          *sc_hash_new (sc_hash_function_t hash_fn,
+                                 sc_equal_function_t equal_fn,
+                                 void *user_data, sc_mempool_t * allocator);
+
+/** Destroy a hash table.
+ *
+ * If the allocator is owned, this runs in O(1), otherwise in O(N).
+ * \note If allocator was provided in sc_hash_new, it will not be destroyed.
+ */
+void                sc_hash_destroy (sc_hash_t * hash);
+
+/** Remove all entries from a hash table in O(N).
+ *
+ * If the allocator is owned, it calls sc_hash_unlink and sc_mempool_truncate.
+ * Otherwise, it calls sc_list_reset on every hash slot which is slower.
+ */
+void                sc_hash_truncate (sc_hash_t * hash);
+
+/** Unlink all hash elements without returning them to the mempool.
+ *
+ * If the allocator is not owned, this runs faster than sc_hash_truncate,
+ *    but is dangerous because of potential memory leaks.
+ * \param [in,out]  hash       Hash structure to be unlinked.
+ */
+void                sc_hash_unlink (sc_hash_t * hash);
+
+/** Same effect as unlink and destroy, but in O(1).
+ * This is dangerous because of potential memory leaks.
+ * \param [in]  hash       Hash structure to be unlinked and destroyed.
+ */
+void                sc_hash_unlink_destroy (sc_hash_t * hash);
+
+/** Check if an object is contained in the hash table.
+ * \param [in]  v      The object to be looked up.
+ * \param [out] found  If found != NULL, *found is set to the address of the
+ *                     pointer to the already contained object if the object
+ *                     is found.  You can assign to **found to override.
+ * \return Returns true if object is found, false otherwise.
+ */
+int                 sc_hash_lookup (sc_hash_t * hash, void *v, void ***found);
+
+/** Insert an object into a hash table if it is not contained already.
+ * \param [in]  v      The object to be inserted.
+ * \param [out] found  If found != NULL, *found is set to the address of the
+ *                     pointer to the already contained, or if not present,
+ *                     the new object.  You can assign to **found to override.
+ * \return Returns true if object is added, false if it is already contained.
+ */
+int                 sc_hash_insert_unique (sc_hash_t * hash, void *v,
+                                           void ***found);
+
+/** Remove an object from a hash table.
+ * \param [in]  v      The object to be removed.
+ * \param [out] found  If found != NULL, *found is set to the object
+                       that is removed if that exists.
+ * \return Returns true if object is found, false if is not contained.
+ */
+int                 sc_hash_remove (sc_hash_t * hash, void *v, void **found);
+
+/** Invoke a callback for every member of the hash table.
+ * The functions hash_fn and equal_fn are not called by this function.
+ */
+void                sc_hash_foreach (sc_hash_t * hash, sc_hash_foreach_t fn);
+
+/** Compute and print statistical information about the occupancy.
+ */
+void                sc_hash_print_statistics (int package_id,
+                                              int log_priority,
+                                              sc_hash_t * hash);
+
+typedef struct sc_hash_array_data
+{
+  sc_array_t         *pa;
+  sc_hash_function_t  hash_fn;
+  sc_equal_function_t equal_fn;
+  void               *user_data;
+  void               *current_item;
+}
+sc_hash_array_data_t;
+
+/** The sc_hash_array implements an array backed up by a hash table.
+ * This enables O(1) access for array elements.
+ */
+typedef struct sc_hash_array
+{
+  /* implementation variables */
+  sc_array_t          a;
+  sc_hash_array_data_t internal_data;
+  sc_hash_t          *h;
+}
+sc_hash_array_t;
+
+/** Calculate the memory used by a hash array.
+ * \param [in] ha          The hash array.
+ * \return                 Memory used in bytes.
+ */
+size_t              sc_hash_array_memory_used (sc_hash_array_t * ha);
+
+/** Create a new hash array.
+ * \param [in] elem_size   Size of one array element in bytes.
+ * \param [in] hash_fn     Function to compute the hash value.
+ * \param [in] equal_fn    Function to test two objects for equality.
+ */
+sc_hash_array_t    *sc_hash_array_new (size_t elem_size,
+                                       sc_hash_function_t hash_fn,
+                                       sc_equal_function_t equal_fn,
+                                       void *user_data);
+
+/** Destroy a hash array.
+ */
+void                sc_hash_array_destroy (sc_hash_array_t * hash_array);
+
+/** Check the internal consistency of a hash array.
+ */
+int                 sc_hash_array_is_valid (sc_hash_array_t * hash_array);
+
+/** Remove all elements from the hash array.
+ * \param [in,out] hash_array   Hash array to truncate.
+ */
+void                sc_hash_array_truncate (sc_hash_array_t * hash_array);
+
+/** Check if an object is contained in a hash array.
+ *
+ * \param [in]  v          A pointer to the object.
+ * \param [out] position   If position != NULL, *position is set to the
+ *                         array position of the already contained object
+ *                         if found.
+ * \return                 Returns true if object is found, false otherwise.
+ */
+int                 sc_hash_array_lookup (sc_hash_array_t * hash_array,
+                                          void *v, size_t * position);
+
+/** Insert an object into a hash array if it is not contained already.
+ * The object is not copied into the array.  Use the return value for that.
+ * New objects are guaranteed to be added at the end of the array.
+ *
+ * \param [in]  v          A pointer to the object.  Used for search only.
+ * \param [out] position   If position != NULL, *position is set to the
+ *                         array position of the already contained, or if
+ *                         not present, the new object.
+ * \return                 Returns NULL if the object is already contained.
+ *                         Otherwise returns its new address in the array.
+ */
+void               *sc_hash_array_insert_unique (sc_hash_array_t * hash_array,
+                                                 void *v, size_t * position);
+
+/** Extract the array data from a hash array and destroy everything else.
+ * \param [in] hash_array   The hash array is destroyed after extraction.
+ * \param [in] rip          Array structure that will be overwritten.
+ *                          All previous array data (if any) will be leaked.
+ *                          The filled array can be freed with sc_array_reset.
+ */
+void                sc_hash_array_rip (sc_hash_array_t * hash_array,
+                                       sc_array_t * rip);
+
+/** The sc_recycle_array object provides an array of slots that can be reused.
+ *
+ * It keeps a list of free slots in the array which will be used for insertion
+ * while available.  Otherwise, the array is grown.
+ */
+typedef struct sc_recycle_array
+{
+  /* interface variables */
+  size_t              elem_count;       /* number of valid entries */
+
+  /* implementation variables */
+  sc_array_t          a;
+  sc_array_t          f;
+}
+sc_recycle_array_t;
+
+/** Initialize a recycle array.
+ *
+ * \param [in] elem_size   Size of the objects to be stored in the array.
+ */
+void                sc_recycle_array_init (sc_recycle_array_t * rec_array,
+                                           size_t elem_size);
+
+/** Reset a recycle array.
+ *
+ * As with all _reset functions, calling _init, then any array operations,
+ * then _reset is memory neutral.
+ */
+void                sc_recycle_array_reset (sc_recycle_array_t * rec_array);
+
+/** Insert an object into the recycle array.
+ * The object is not copied into the array.  Use the return value for that.
+ *
+ * \param [out] position   If position != NULL, *position is set to the
+ *                         array position of the inserted object.
+ * \return                 Returns the new address of the object in the array.
+ */
+void               *sc_recycle_array_insert (sc_recycle_array_t * rec_array,
+                                             size_t * position);
+
+/** Remove an object from the recycle array.  It must be valid.
+ *
+ * \param [in] position   Index into the array for the object to remove.
+ * \return                The pointer to the removed object.  Will be valid
+ *                        as long as no other function is called
+ *                        on this recycle array.
+ */
+void               *sc_recycle_array_remove (sc_recycle_array_t * rec_array,
+                                             size_t position);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_CONTAINERS_H */
diff --git a/sc/src/sc_dmatrix.c b/sc/src/sc_dmatrix.c
new file mode 100644
index 0000000..53c461f
--- /dev/null
+++ b/sc/src/sc_dmatrix.c
@@ -0,0 +1,749 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_dmatrix.h>
+#include <sc_lapack.h>
+
+int
+sc_darray_is_valid (const double *darray, size_t nelem)
+{
+  size_t              zz;
+
+  for (zz = 0; zz < nelem; ++zz) {
+    if (darray[zz] != darray[zz]) {     /* ignore the comparison warning */
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+int
+sc_darray_is_range (const double *darray, size_t nelem,
+                    double low, double high)
+{
+  size_t              zz;
+
+  for (zz = 0; zz < nelem; ++zz) {
+    if (!(low <= darray[zz] && darray[zz] <= high)) {
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+size_t
+sc_dmatrix_memory_used (sc_dmatrix_t * dm)
+{
+  size_t              mem = sizeof (sc_dmatrix_t);
+
+  mem += (dm->m + 1) * sizeof (double *);
+  if (!dm->view) {
+    mem += dm->m * dm->n * sizeof (double);
+  }
+
+  return mem;
+}
+
+static void
+sc_dmatrix_new_e (sc_dmatrix_t * rdm, sc_bint_t m, sc_bint_t n, double *data)
+{
+  sc_bint_t           i;
+
+  SC_ASSERT (m >= 0 && n >= 0);
+  SC_ASSERT (rdm != NULL);
+
+  rdm->e = SC_ALLOC (double *, m + 1);
+  rdm->e[0] = data;
+
+  if (m > 0) {
+    for (i = 1; i < m; ++i)
+      rdm->e[i] = rdm->e[i - 1] + n;
+
+    rdm->e[m] = NULL;           /* safeguard */
+  }
+
+  rdm->m = m;
+  rdm->n = n;
+}
+
+static sc_dmatrix_t *
+sc_dmatrix_new_internal (sc_bint_t m, sc_bint_t n, int init_zero)
+{
+  sc_dmatrix_t       *rdm;
+  double             *data;
+  size_t              size = (size_t) (m * n);
+#ifdef SC_DEBUG
+  double              zero = 0.0;       /* no const to avoid warning */
+  const double        anan = 0.0 / zero;
+  size_t              zz;
+#endif
+
+  SC_ASSERT (m >= 0 && n >= 0);
+
+  rdm = SC_ALLOC (sc_dmatrix_t, 1);
+
+  if (init_zero) {
+    data = SC_ALLOC_ZERO (double, size);
+  }
+  else {
+    data = SC_ALLOC (double, size);
+#ifdef SC_DEBUG
+    /* In debug mode initialize the memory to NaN. */
+    for (zz = 0; zz < size; ++zz) {
+      data[zz] = anan;
+    }
+#endif
+  }
+
+  sc_dmatrix_new_e (rdm, m, n, data);
+  rdm->view = 0;
+
+  return rdm;
+}
+
+sc_dmatrix_t       *
+sc_dmatrix_new (sc_bint_t m, sc_bint_t n)
+{
+  return sc_dmatrix_new_internal (m, n, 0);
+}
+
+sc_dmatrix_t       *
+sc_dmatrix_new_zero (sc_bint_t m, sc_bint_t n)
+{
+  return sc_dmatrix_new_internal (m, n, 1);
+}
+
+sc_dmatrix_t       *
+sc_dmatrix_new_data (sc_bint_t m, sc_bint_t n, double *data)
+{
+  sc_dmatrix_t       *rdm;
+
+  SC_ASSERT (m >= 0 && n >= 0);
+
+  rdm = SC_ALLOC (sc_dmatrix_t, 1);
+  sc_dmatrix_new_e (rdm, m, n, data);
+  rdm->view = 1;
+
+  return rdm;
+}
+
+sc_dmatrix_t       *
+sc_dmatrix_new_view (sc_bint_t m, sc_bint_t n, sc_dmatrix_t * orig)
+{
+  return sc_dmatrix_new_view_offset (0, m, n, orig);
+}
+
+sc_dmatrix_t       *
+sc_dmatrix_new_view_offset (sc_bint_t o, sc_bint_t m, sc_bint_t n,
+                            sc_dmatrix_t * orig)
+{
+  sc_dmatrix_t       *rdm;
+
+  SC_ASSERT (o >= 0 && m >= 0 && n >= 0);
+  SC_ASSERT ((o + m) * n <= orig->m * orig->n);
+
+  rdm = SC_ALLOC (sc_dmatrix_t, 1);
+  sc_dmatrix_new_e (rdm, m, n, orig->e[0] + o * n);
+  rdm->view = 1;
+
+  return rdm;
+}
+
+sc_dmatrix_t       *
+sc_dmatrix_clone (const sc_dmatrix_t * X)
+{
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *Xdata = X->e[0];
+  double             *Ydata;
+  sc_dmatrix_t       *clone;
+
+  clone = sc_dmatrix_new (X->m, X->n);
+  Ydata = clone->e[0];
+
+  memcpy (Ydata, Xdata, totalsize * sizeof (double));
+
+  return clone;
+}
+
+void
+sc_dmatrix_reshape (sc_dmatrix_t * dmatrix, sc_bint_t m, sc_bint_t n)
+{
+  double             *data;
+
+  SC_ASSERT (dmatrix->e != NULL);
+  SC_ASSERT (dmatrix->m * dmatrix->n == m * n);
+
+  data = dmatrix->e[0];
+  SC_FREE (dmatrix->e);
+  sc_dmatrix_new_e (dmatrix, m, n, data);
+}
+
+void
+sc_dmatrix_resize (sc_dmatrix_t * dmatrix, sc_bint_t m, sc_bint_t n)
+{
+  double             *data;
+  sc_bint_t           size, newsize;
+
+  SC_ASSERT (dmatrix->e != NULL);
+  SC_ASSERT (m >= 0 && n >= 0);
+
+  size = dmatrix->m * dmatrix->n;
+  newsize = m * n;
+
+  if (!dmatrix->view && size != newsize) {
+    data = SC_REALLOC (dmatrix->e[0], double, newsize);
+  }
+  else {
+    /* for views you must know that data is large enough */
+    data = dmatrix->e[0];
+  }
+  SC_FREE (dmatrix->e);
+  sc_dmatrix_new_e (dmatrix, m, n, data);
+}
+
+void
+sc_dmatrix_resize_in_place (sc_dmatrix_t * dmatrix, sc_bint_t m, sc_bint_t n)
+{
+  double             *data;
+  sc_bint_t           size, newsize;
+  sc_bint_t           i;
+  sc_bint_t           old_n = dmatrix->n;
+  sc_bint_t           min_m = SC_MIN (m, dmatrix->m);
+
+  SC_ASSERT (dmatrix->e != NULL);
+  SC_ASSERT (m >= 0 && n >= 0);
+  SC_ASSERT (!dmatrix->view);
+
+  size = dmatrix->m * dmatrix->n;
+  newsize = m * n;
+  data = dmatrix->e[0];
+  if (n < old_n) {
+    for (i = 1; i < min_m; i++) {
+      memmove (data + i * n, data + i * old_n, n * sizeof (double));
+    }
+  }
+  if (newsize != size) {
+    data = SC_REALLOC (dmatrix->e[0], double, newsize);
+  }
+  if (n > old_n) {
+    for (i = min_m - 1; i > 0; i--) {
+      memmove (data + i * n, data + i * old_n, old_n * sizeof (double));
+    }
+  }
+  SC_FREE (dmatrix->e);
+  sc_dmatrix_new_e (dmatrix, m, n, data);
+}
+
+void
+sc_dmatrix_destroy (sc_dmatrix_t * dmatrix)
+{
+  if (!dmatrix->view) {
+    SC_FREE (dmatrix->e[0]);
+  }
+  SC_FREE (dmatrix->e);
+
+  SC_FREE (dmatrix);
+}
+
+int
+sc_dmatrix_is_valid (const sc_dmatrix_t * A)
+{
+  return sc_darray_is_valid (A->e[0], (size_t) A->m * (size_t) A->n);
+}
+
+int
+sc_dmatrix_is_symmetric (const sc_dmatrix_t * A, double tolerance)
+{
+  sc_bint_t           i, j;
+  double              diff;
+
+  SC_ASSERT (A->m == A->n);
+
+  for (i = 0; i < A->n; ++i) {
+    for (j = i + 1; j < A->n; ++j) {
+      diff = fabs (A->e[i][j] - A->e[j][i]);
+      if (diff > tolerance) {
+        SC_LDEBUGF ("sc_dmatrix not symmetric by %g\n", diff);
+
+        return 0;
+      }
+    }
+  }
+
+  return 1;
+}
+
+void
+sc_dmatrix_set_zero (sc_dmatrix_t * X)
+{
+  sc_dmatrix_set_value (X, 0.0);
+}
+
+void
+sc_dmatrix_set_value (sc_dmatrix_t * X, double value)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  double             *data = X->e[0];
+
+  for (i = 0; i < totalsize; ++i)
+    data[i] = value;
+}
+
+void
+sc_dmatrix_scale (double alpha, sc_dmatrix_t * X)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  double             *Xdata = X->e[0];
+
+  for (i = 0; i < totalsize; ++i)
+    Xdata[i] *= alpha;
+}
+
+void
+sc_dmatrix_shift (double alpha, sc_dmatrix_t * X)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  double             *Xdata = X->e[0];
+
+  for (i = 0; i < totalsize; ++i)
+    Xdata[i] += alpha;
+}
+
+void
+sc_dmatrix_alphadivide (double alpha, sc_dmatrix_t * X)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  double             *Xdata = X->e[0];
+
+  for (i = 0; i < totalsize; ++i)
+    Xdata[i] = alpha / Xdata[i];
+}
+
+void
+sc_dmatrix_pow (double alpha, sc_dmatrix_t * X)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  double             *Xdata = X->e[0];
+
+  for (i = 0; i < totalsize; ++i)
+    Xdata[i] = pow (Xdata[i], alpha);
+}
+
+void
+sc_dmatrix_fabs (const sc_dmatrix_t * X, sc_dmatrix_t * Y)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *Xdata = X->e[0];
+  double             *Ydata = Y->e[0];
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  for (i = 0; i < totalsize; ++i)
+    Ydata[i] = fabs (Xdata[i]);
+}
+
+void
+sc_dmatrix_sqrt (const sc_dmatrix_t * X, sc_dmatrix_t * Y)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *Xdata = X->e[0];
+  double             *Ydata = Y->e[0];
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  for (i = 0; i < totalsize; ++i)
+    Ydata[i] = sqrt (Xdata[i]);
+}
+
+void
+sc_dmatrix_getsign (const sc_dmatrix_t * X, sc_dmatrix_t * Y)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *indata = X->e[0];
+  double             *outdata = Y->e[0];
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  for (i = 0; i < totalsize; ++i)
+    outdata[i] = (indata[i] >= 0. ? 1 : -1);
+}
+
+void
+sc_dmatrix_greaterequal (const sc_dmatrix_t * X, double bound,
+                         sc_dmatrix_t * Y)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *indata = X->e[0];
+  double             *outdata = Y->e[0];
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  for (i = 0; i < totalsize; ++i)
+    outdata[i] = (indata[i] >= bound ? 1 : 0);
+}
+
+void
+sc_dmatrix_lessequal (const sc_dmatrix_t * X, double bound, sc_dmatrix_t * Y)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *indata = X->e[0];
+  double             *outdata = Y->e[0];
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  for (i = 0; i < totalsize; ++i)
+    outdata[i] = (indata[i] <= bound ? 1 : 0);
+}
+
+void
+sc_dmatrix_maximum (const sc_dmatrix_t * X, sc_dmatrix_t * Y)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *indata = X->e[0];
+  double             *outdata = Y->e[0];
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  for (i = 0; i < totalsize; ++i)
+    outdata[i] = SC_MAX (indata[i], outdata[i]);
+}
+
+void
+sc_dmatrix_minimum (const sc_dmatrix_t * X, sc_dmatrix_t * Y)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *indata = X->e[0];
+  double             *outdata = Y->e[0];
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  for (i = 0; i < totalsize; ++i)
+    outdata[i] = SC_MIN (indata[i], outdata[i]);
+}
+
+void
+sc_dmatrix_dotmultiply (const sc_dmatrix_t * X, sc_dmatrix_t * Y)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *Xdata = X->e[0];
+  double             *Ydata = Y->e[0];
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  for (i = 0; i < totalsize; ++i)
+    Ydata[i] *= Xdata[i];
+}
+
+void
+sc_dmatrix_dotdivide (const sc_dmatrix_t * X, sc_dmatrix_t * Y)
+{
+  sc_bint_t           i;
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *Xdata = X->e[0];
+  double             *Ydata = Y->e[0];
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  for (i = 0; i < totalsize; ++i)
+    Ydata[i] /= Xdata[i];
+}
+
+void
+sc_dmatrix_copy (const sc_dmatrix_t * X, sc_dmatrix_t * Y)
+{
+  const sc_bint_t     totalsize = X->m * X->n;
+  const double       *Xdata = X->e[0];
+  double             *Ydata = Y->e[0];
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  memmove (Ydata, Xdata, totalsize * sizeof (double));
+}
+
+void
+sc_dmatrix_transpose (const sc_dmatrix_t * X, sc_dmatrix_t * Y)
+{
+  sc_bint_t           i, j, Xrows, Xcols, Xstride, Ystride;
+  double             *Ydata;
+  const double       *Xdata = X->e[0];
+
+  SC_ASSERT (X->m == Y->n && X->n == Y->m);
+
+  Xrows = X->m;
+  Xcols = X->n;
+  Xstride = X->n;
+  Ystride = Y->n;
+  Ydata = Y->e[0];
+
+  for (i = 0; i < Xrows; i++) {
+    for (j = 0; j < Xcols; j++) {
+      Ydata[j * Ystride + i] = Xdata[i * Xstride + j];
+    }
+  }
+}
+
+void
+sc_dmatrix_add (double alpha, const sc_dmatrix_t * X, sc_dmatrix_t * Y)
+{
+  sc_bint_t           totalsize, inc;
+
+  SC_ASSERT (X->m == Y->m && X->n == Y->n);
+
+  totalsize = X->m * X->n;
+
+  inc = 1;
+  if (totalsize > 0) {
+    SC_BLAS_DAXPY (&totalsize, &alpha, X->e[0], &inc, Y->e[0], &inc);
+  }
+}
+
+void
+sc_dmatrix_vector (sc_trans_t transa, sc_trans_t transx, sc_trans_t transy,
+                   double alpha, const sc_dmatrix_t * A,
+                   const sc_dmatrix_t * X, double beta, sc_dmatrix_t * Y)
+{
+  sc_bint_t           inc = 1;
+
+#ifdef SC_DEBUG
+  sc_bint_t           dimX = (transx == SC_NO_TRANS) ? X->m : X->n;
+  sc_bint_t           dimY = (transy == SC_NO_TRANS) ? Y->m : Y->n;
+  sc_bint_t           dimX1 = (transx == SC_NO_TRANS) ? X->n : X->m;
+  sc_bint_t           dimY1 = (transy == SC_NO_TRANS) ? Y->n : Y->m;
+
+  sc_bint_t           Arows = (transa == SC_NO_TRANS) ? A->m : A->n;
+  sc_bint_t           Acols = (transa == SC_NO_TRANS) ? A->n : A->m;
+#endif
+
+  SC_ASSERT (Acols == dimX && Arows == dimY);
+  SC_ASSERT (dimX1 == 1 && dimY1 == 1);
+
+  if (A->n > 0 && A->m > 0) {
+    SC_BLAS_DGEMV (&sc_antitranschar[transa], &A->n, &A->m, &alpha,
+                   A->e[0], &A->n, X->e[0], &inc, &beta, Y->e[0], &inc);
+  }
+  else if (beta != 1.) {
+    sc_dmatrix_scale (beta, Y);
+  }
+}
+
+void
+sc_dmatrix_multiply (sc_trans_t transa, sc_trans_t transb, double alpha,
+                     const sc_dmatrix_t * A, const sc_dmatrix_t * B,
+                     double beta, sc_dmatrix_t * C)
+{
+  sc_bint_t           Acols, Crows, Ccols;
+#ifdef SC_DEBUG
+  sc_bint_t           Arows, Brows, Bcols;
+
+  Arows = (transa == SC_NO_TRANS) ? A->m : A->n;
+  Brows = (transb == SC_NO_TRANS) ? B->m : B->n;
+  Bcols = (transb == SC_NO_TRANS) ? B->n : B->m;
+#endif
+
+  Acols = (transa == SC_NO_TRANS) ? A->n : A->m;
+  Crows = C->m;
+  Ccols = C->n;
+
+  SC_ASSERT (Acols == Brows && Arows == Crows && Bcols == Ccols);
+  SC_ASSERT (transa == SC_NO_TRANS || transa == SC_TRANS);
+  SC_ASSERT (transb == SC_NO_TRANS || transb == SC_TRANS);
+
+  if (Crows > 0 && Ccols > 0) {
+    if (Acols > 0) {
+      SC_BLAS_DGEMM (&sc_transchar[transb], &sc_transchar[transa], &Ccols,
+                     &Crows, &Acols, &alpha, B->e[0], &B->n, A->e[0], &A->n,
+                     &beta, C->e[0], &C->n);
+    }
+    else if (beta != 1.0) {     /* ignore comparison warning */
+      sc_dmatrix_scale (beta, C);
+    }
+  }
+}
+
+void
+sc_dmatrix_ldivide (sc_trans_t transa, const sc_dmatrix_t * A,
+                    const sc_dmatrix_t * B, sc_dmatrix_t * C)
+{
+  sc_dmatrix_t       *BT;
+  sc_trans_t          invtransa =
+    (transa == SC_NO_TRANS) ? SC_TRANS : SC_NO_TRANS;
+
+#ifdef SC_DEBUG
+  sc_bint_t           A_nrows = (transa == SC_NO_TRANS) ? A->m : A->n;
+  sc_bint_t           A_ncols = (transa == SC_NO_TRANS) ? A->n : A->m;
+  sc_bint_t           B_nrows = B->m;
+  sc_bint_t           B_ncols = B->n;
+  sc_bint_t           C_nrows = C->m;
+  sc_bint_t           C_ncols = C->n;
+#endif
+
+  SC_ASSERT ((C_nrows == A_ncols) && (B_nrows == A_nrows)
+             && (B_ncols == C_ncols));
+
+  BT = sc_dmatrix_new (B->n, B->m);
+  sc_dmatrix_transpose (B, BT);
+
+  sc_dmatrix_rdivide (invtransa, BT, A, BT);
+
+  sc_dmatrix_transpose (BT, C);
+
+  sc_dmatrix_destroy (BT);
+}
+
+void
+sc_dmatrix_rdivide (sc_trans_t transb, const sc_dmatrix_t * A,
+                    const sc_dmatrix_t * B, sc_dmatrix_t * C)
+{
+  sc_bint_t           A_nrows = A->m;
+  sc_bint_t           B_nrows = (transb == SC_NO_TRANS) ? B->m : B->n;
+  sc_bint_t           B_ncols = (transb == SC_NO_TRANS) ? B->n : B->m;
+#ifdef SC_DEBUG
+  sc_bint_t           A_ncols = A->n;
+  sc_bint_t           C_nrows = C->m;
+  sc_bint_t           C_ncols = C->n;
+#endif
+  sc_bint_t           M = B_ncols, N = B_nrows, Nrhs = A_nrows, info = 0;
+
+  SC_ASSERT ((C_nrows == A_nrows) && (B_nrows == C_ncols)
+             && (B_ncols == A_ncols));
+  SC_ASSERT (N > 0 && Nrhs > 0);
+
+  if (M == N) {
+    sc_dmatrix_t       *lu = sc_dmatrix_clone (B);
+    sc_bint_t          *ipiv = SC_ALLOC (sc_bint_t, N);
+
+    /* Perform an LU factorization of B. */
+    SC_LAPACK_DGETRF (&N, &N, lu->e[0], &N, ipiv, &info);
+
+    SC_ASSERT (info == 0);
+
+    /* Solve the linear system. */
+    sc_dmatrix_copy (A, C);
+    SC_LAPACK_DGETRS (&sc_transchar[transb], &N, &Nrhs, lu->e[0], &N,
+                      ipiv, C->e[0], &N, &info);
+
+    SC_ASSERT (info == 0);
+
+    SC_FREE (ipiv);
+    sc_dmatrix_destroy (lu);
+  }
+  else {
+    SC_CHECK_ABORT (0, "Only square A's work right now\n");
+  }
+}
+
+void
+sc_dmatrix_write (const sc_dmatrix_t * dmatrix, FILE * fp)
+{
+  sc_bint_t           i, j, m, n;
+
+  m = dmatrix->m;
+  n = dmatrix->n;
+
+  for (i = 0; i < m; ++i) {
+    for (j = 0; j < n; ++j) {
+      fprintf (fp, " %16.8e", dmatrix->e[i][j]);
+    }
+    fprintf (fp, "\n");
+  }
+}
+
+sc_dmatrix_pool_t  *
+sc_dmatrix_pool_new (int m, int n)
+{
+  sc_dmatrix_pool_t  *dmpool;
+
+  SC_ASSERT (m >= 0 && n >= 0);
+
+  dmpool = SC_ALLOC (sc_dmatrix_pool_t, 1);
+
+  dmpool->m = m;
+  dmpool->n = n;
+  dmpool->elem_count = 0;
+  sc_array_init (&dmpool->freed, sizeof (sc_dmatrix_t *));
+
+  return dmpool;
+}
+
+void
+sc_dmatrix_pool_destroy (sc_dmatrix_pool_t * dmpool)
+{
+  size_t              zz;
+  sc_dmatrix_t      **pdm;
+
+  SC_ASSERT (dmpool->elem_count == 0);
+
+  for (zz = 0; zz < dmpool->freed.elem_count; ++zz) {
+    pdm = (sc_dmatrix_t **) sc_array_index (&dmpool->freed, zz);
+    sc_dmatrix_destroy (*pdm);
+  }
+  sc_array_reset (&dmpool->freed);
+
+  SC_FREE (dmpool);
+}
+
+sc_dmatrix_t       *
+sc_dmatrix_pool_alloc (sc_dmatrix_pool_t * dmpool)
+{
+  sc_dmatrix_t       *dm;
+
+  ++dmpool->elem_count;
+
+  if (dmpool->freed.elem_count > 0) {
+    dm = *(sc_dmatrix_t **) sc_array_pop (&dmpool->freed);
+  }
+  else {
+    dm = sc_dmatrix_new (dmpool->m, dmpool->n);
+  }
+
+#ifdef SC_DEBUG
+  sc_dmatrix_set_value (dm, -1.);
+#endif
+
+  return dm;
+}
+
+void
+sc_dmatrix_pool_free (sc_dmatrix_pool_t * dmpool, sc_dmatrix_t * dm)
+{
+  SC_ASSERT (dmpool->elem_count > 0);
+  SC_ASSERT (dm->m == dmpool->m && dm->n == dmpool->n);
+
+  --dmpool->elem_count;
+
+  *(sc_dmatrix_t **) sc_array_push (&dmpool->freed) = dm;
+}
diff --git a/sc/src/sc_dmatrix.h b/sc/src/sc_dmatrix.h
new file mode 100644
index 0000000..53931d4
--- /dev/null
+++ b/sc/src/sc_dmatrix.h
@@ -0,0 +1,314 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_DMATRIX_H
+#define SC_DMATRIX_H
+
+#include <sc_blas.h>
+#include <sc_containers.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef struct sc_dmatrix
+{
+  double            **e;
+  sc_bint_t           m, n;
+  int                 view;
+}
+sc_dmatrix_t;
+
+/** Check whether a double array is free of NaN entries.
+ * \param [in] darray   Array of doubles.
+ * \param [in] nelem    Number of doubles in the array.
+ * \return              Return false if at least one entry is NaN.
+ */
+int                 sc_darray_is_valid (const double *darray, size_t nelem);
+
+/** Check whether the values in a double array are in a certain range.
+ * \param [in] darray   Array of doubles.
+ * \param [in] nelem    Number of doubles in the array.
+ * \param [in] low      Lowest allowed value in the array.
+ * \param [in] high     Highest allowed value in the array.
+ * \return              Return false if at least one entry is out of range.
+ */
+int                 sc_darray_is_range (const double *darray, size_t nelem,
+                                        double low, double high);
+
+/** Calculate the memory used by a dmatrix.
+ * \param [in] array       The dmatrix.
+ * \return                 Memory used in bytes.
+ */
+size_t              sc_dmatrix_memory_used (sc_dmatrix_t * dmatrix);
+
+/*
+ * The sc_dmatrix_new/clone functions abort on allocation errors.
+ * There is no need to check the return value.
+ */
+sc_dmatrix_t       *sc_dmatrix_new (sc_bint_t m, sc_bint_t n);
+sc_dmatrix_t       *sc_dmatrix_new_zero (sc_bint_t m, sc_bint_t n);
+sc_dmatrix_t       *sc_dmatrix_clone (const sc_dmatrix_t * dmatrix);
+
+/** Create a matrix view on an existing data array.
+ * The data array must have been previously allocated and large enough.
+ * The data array must not be deallocated while the view is in use.
+ */
+sc_dmatrix_t       *sc_dmatrix_new_data (sc_bint_t m, sc_bint_t n,
+                                         double *data);
+
+/** Create a matrix view on an existing sc_dmatrix_t.
+ * The original matrix must have greater equal as many elements as the view.
+ * The original matrix must not be destroyed or resized while view is in use.
+ */
+sc_dmatrix_t       *sc_dmatrix_new_view (sc_bint_t m, sc_bint_t n,
+                                         sc_dmatrix_t * orig);
+
+/** Create a matrix view on an existing sc_dmatrix_t.
+ * The start of the view is offset by a number of rows.
+ * The original matrix must have greater equal as many elements as view end.
+ * The original matrix must not be destroyed or resized while view is in use.
+ * \param[in] o     Number of rows that the view is offset.
+ *                  Requires (o + m) * n <= orig->m * orig->n.
+ */
+sc_dmatrix_t       *sc_dmatrix_new_view_offset (sc_bint_t o,
+                                                sc_bint_t m, sc_bint_t n,
+                                                sc_dmatrix_t * orig);
+
+/** Reshape a matrix to different m and n without changing m * n.
+ */
+void                sc_dmatrix_reshape (sc_dmatrix_t * dmatrix, sc_bint_t m,
+                                        sc_bint_t n);
+
+/** Change the matrix dimensions.
+ * For views it must be known that the new size is permitted.
+ * For non-views the data will be realloced if necessary.
+ * The entries are unchanged to the minimum of the old and new sizes.
+ */
+void                sc_dmatrix_resize (sc_dmatrix_t * dmatrix,
+                                       sc_bint_t m, sc_bint_t n);
+
+/** Change the matrix dimensions, while keeping the subscripts in place, i.e.
+ * dmatrix->e[i][j] will have the same value before and after, as long as
+ * (i, j) is still a valid subscript.
+ * This is not valid for views.
+ * For non-views the data will be realloced if necessary.
+ * The entries are unchanged to the minimum of the old and new sizes.
+ */
+void                sc_dmatrix_resize_in_place (sc_dmatrix_t * dmatrix,
+                                                sc_bint_t m, sc_bint_t n);
+
+/** Destroy a dmatrix and all allocated memory */
+void                sc_dmatrix_destroy (sc_dmatrix_t * dmatrix);
+
+/** Check whether a dmatrix is free of NaN entries.
+ * \return          true if the dmatrix does not contain any NaN entries.
+ */
+int                 sc_dmatrix_is_valid (const sc_dmatrix_t * A);
+
+/** Check a square dmatrix for symmetry.
+ * \param [in] tolerance    measures the absolute value of the max difference.
+ * \return                  true if matrix is numerically symmetric.
+ */
+int                 sc_dmatrix_is_symmetric (const sc_dmatrix_t * A,
+                                             double tolerance);
+
+void                sc_dmatrix_set_zero (sc_dmatrix_t * dmatrix);
+void                sc_dmatrix_set_value (sc_dmatrix_t * dmatrix,
+                                          double value);
+
+/** Perform element-wise multiplication with a scalar, X := alpha .* X.
+ */
+void                sc_dmatrix_scale (double alpha, sc_dmatrix_t * X);
+
+/** Perform element-wise addition with a scalar, X := X + alpha.
+ */
+void                sc_dmatrix_shift (double alpha, sc_dmatrix_t * X);
+
+/** Perform element-wise divison with a scalar, X := alpha ./ X.
+ */
+void                sc_dmatrix_alphadivide (double alpha, sc_dmatrix_t * X);
+
+/** Perform element-wise exponentiation with a scalar, X := X ^ alpha.
+ */
+void                sc_dmatrix_pow (double exponent, sc_dmatrix_t * X);
+
+/** Perform element-wise absolute value, Y := fabs(X).
+ */
+void                sc_dmatrix_fabs (const sc_dmatrix_t * X,
+                                     sc_dmatrix_t * Y);
+
+/** Perform element-wise square root, Y := sqrt(X).
+ */
+void                sc_dmatrix_sqrt (const sc_dmatrix_t * X,
+                                     sc_dmatrix_t * Y);
+
+/** Extract the element-wise sign of a matrix, Y := (X >= 0 ? 1 : -1)
+ */
+void                sc_dmatrix_getsign (const sc_dmatrix_t * X,
+                                        sc_dmatrix_t * Y);
+
+/** Compare a matrix element-wise against a bound, Y := (X >= bound ? 1 : 0)
+ */
+void                sc_dmatrix_greaterequal (const sc_dmatrix_t * X,
+                                             double bound, sc_dmatrix_t * Y);
+
+/** Compare a matrix element-wise against a bound, Y := (X <= bound ? 1 : 0)
+ */
+void                sc_dmatrix_lessequal (const sc_dmatrix_t * X,
+                                          double bound, sc_dmatrix_t * Y);
+
+/** Assign element-wise maximum, Y_i := (X_i > Y_i ? X_i : Y_i)
+ */
+void                sc_dmatrix_maximum (const sc_dmatrix_t * X,
+                                        sc_dmatrix_t * Y);
+
+/** Assign element-wise minimum, Y_i := (X_i < Y_i ? X_i : Y_i)
+ */
+void                sc_dmatrix_minimum (const sc_dmatrix_t * X,
+                                        sc_dmatrix_t * Y);
+
+/** Perform element-wise multiplication, Y := Y .* X.
+ */
+void                sc_dmatrix_dotmultiply (const sc_dmatrix_t * X,
+                                            sc_dmatrix_t * Y);
+
+/** Perform element-wise division, Y := Y ./ X.
+ */
+void                sc_dmatrix_dotdivide (const sc_dmatrix_t * X,
+                                          sc_dmatrix_t * Y);
+
+void                sc_dmatrix_copy (const sc_dmatrix_t * X,
+                                     sc_dmatrix_t * Y);
+
+void                sc_dmatrix_transpose (const sc_dmatrix_t * X,
+                                          sc_dmatrix_t * Y);
+
+/*! \brief Matrix Matrix Add (AXPY)  \c Y := alpha X + Y
+ */
+void                sc_dmatrix_add (double alpha, const sc_dmatrix_t * X,
+                                    sc_dmatrix_t * Y);
+
+/**
+ * Perform matrix-vector multiplication Y = alpha * A * X + beta * Y.
+ * \param [in] transa    Transpose operation for matrix A.
+ * \param [in] transx    Transpose operation for matrix X.
+ * \param [in] transy    Transpose operation for matrix Y.
+ * \param [in] A         Matrix.
+ * \param [in] X, Y      Column or row vectors (or one each).
+ */
+void                sc_dmatrix_vector (sc_trans_t transa,
+                                       sc_trans_t transx,
+                                       sc_trans_t transy,
+                                       double alpha, const sc_dmatrix_t * A,
+                                       const sc_dmatrix_t * X, double beta,
+                                       sc_dmatrix_t * Y);
+
+/*! \brief Matrix Matrix Multiply  \c C := alpha * A * B + beta * C
+ *
+ *   \param A matrix
+ *   \param B matrix
+ *   \param C matrix
+ */
+void                sc_dmatrix_multiply (sc_trans_t transa,
+                                         sc_trans_t transb, double alpha,
+                                         const sc_dmatrix_t * A,
+                                         const sc_dmatrix_t * B, double beta,
+                                         sc_dmatrix_t * C);
+
+/** \brief Left Divide \c A \ \c B.
+ * The matrices cannot have 0 rows or columns.
+ * Solves  \c A \c C = \c B or \c A' \c C = \c B.
+ *
+ *   \param transa Use the transpose of \c A
+ *   \param A matrix
+ *   \param B matrix
+ *   \param C matrix
+ */
+void                sc_dmatrix_ldivide (sc_trans_t transa,
+                                        const sc_dmatrix_t * A,
+                                        const sc_dmatrix_t * B,
+                                        sc_dmatrix_t * C);
+
+/** \brief Right Divide \c A / \c B.
+ * The matrices cannot have 0 rows or columns.
+ * Solves  \c A = \c C \c B or \c A = \c C \c B'.
+ *
+ *   \param transb Use the transpose of \c B
+ *   \param A matrix
+ *   \param B matrix
+ *   \param C matrix
+ */
+void                sc_dmatrix_rdivide (sc_trans_t transb,
+                                        const sc_dmatrix_t * A,
+                                        const sc_dmatrix_t * B,
+                                        sc_dmatrix_t * C);
+
+/** \brief Writes a matrix to an opened stream.
+ *
+ *   \param dmatrix Pointer to matrix to write
+ *   \param fp      Pointer to file to write to
+ */
+void                sc_dmatrix_write (const sc_dmatrix_t * dmatrix,
+                                      FILE * fp);
+
+/*
+ * The sc_dmatrix_pool recycles matrices of the same size.
+ */
+typedef struct sc_dmatrix_pool
+{
+  int                 m, n;
+  size_t              elem_count;
+  sc_array_t          freed;    /* buffers the freed elements */
+}
+sc_dmatrix_pool_t;
+
+/** Create a new dmatrix pool.
+ * \param [in] m    Row count of the stored matrices.
+ * \param [in] n    Column count of the stored matrices.
+ * \return          Returns a dmatrix pool that is ready to use.
+ */
+sc_dmatrix_pool_t  *sc_dmatrix_pool_new (int m, int n);
+
+/** Destroy a dmatrix pool.
+ * This will also destroy all matrices stored for reuse.
+ * Requires all allocated matrices to be returned to the pool previously.
+ * \param [in]      The dmatrix pool to destroy.
+ */
+void                sc_dmatrix_pool_destroy (sc_dmatrix_pool_t * dmpool);
+
+/** Allocate a dmatrix from the pool.
+ * Reuses a matrix previously returned to the pool, or allocated a fresh one.
+ * \param [in] pool   The dmatrix pool to use.
+ * \return            Returns a dmatrix of size pool->m by pool->n.
+ */
+sc_dmatrix_t       *sc_dmatrix_pool_alloc (sc_dmatrix_pool_t * dmpool);
+
+/** Return a dmatrix to the pool.
+ * The matrix is stored internally for reuse and not freed in this function.
+ * \param [in] pool   The dmatrix pool to use.
+ * \param [in] dm     The dmatrix pool to return to the pool.
+ */
+void                sc_dmatrix_pool_free (sc_dmatrix_pool_t * dmpool,
+                                          sc_dmatrix_t * dm);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_DMATRIX_H */
diff --git a/sc/src/sc_flops.c b/sc/src/sc_flops.c
new file mode 100644
index 0000000..e88ed07
--- /dev/null
+++ b/sc/src/sc_flops.c
@@ -0,0 +1,130 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_flops.h>
+
+#ifdef SC_PAPI
+#ifdef SC_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <papi.h>
+#endif
+
+void
+sc_flops_papi (float *rtime, float *ptime, long long *flpops, float *mflops)
+{
+#ifdef SC_PAPI
+  int                 retval;
+
+  retval = PAPI_flops (rtime, ptime, flpops, mflops);
+  SC_CHECK_ABORT (retval == PAPI_OK, "Papi not happy");
+#else
+  *rtime = *ptime = *mflops = 0.;
+  *flpops = 0;
+#endif
+}
+
+void
+sc_flops_start (sc_flopinfo_t * fi)
+{
+  float               rtime, ptime, mflops;
+  long long           flpops;
+
+  fi->seconds = sc_MPI_Wtime ();
+  sc_flops_papi (&rtime, &ptime, &flpops, &mflops);     /* ignore results */
+
+  fi->cwtime = 0.;
+  fi->crtime = fi->cptime = 0.;
+  fi->cflpops = 0;
+
+  fi->iwtime = 0.;
+  fi->irtime = fi->iptime = fi->mflops = 0.;
+  fi->iflpops = 0;
+}
+
+void
+sc_flops_count (sc_flopinfo_t * fi)
+{
+  double              seconds;
+  float               rtime, ptime;
+  long long           flpops;
+
+  seconds = sc_MPI_Wtime ();
+  sc_flops_papi (&rtime, &ptime, &flpops, &fi->mflops);
+
+  fi->iwtime = seconds - fi->seconds;
+  fi->cwtime += fi->iwtime;
+
+  fi->iptime = ptime - fi->cptime;
+  fi->cptime = ptime;
+
+  fi->iflpops = flpops - fi->cflpops;
+  fi->cflpops = flpops;
+
+#ifdef SC_PAPI
+  fi->irtime = rtime - fi->crtime;
+  fi->crtime = rtime;
+#else
+  fi->irtime = (float) fi->iwtime;
+  fi->crtime = (float) fi->cwtime;
+#endif
+  fi->seconds = seconds;
+}
+
+void
+sc_flops_snap (sc_flopinfo_t * fi, sc_flopinfo_t * snapshot)
+{
+  sc_flops_count (fi);
+  *snapshot = *fi;
+}
+
+void
+sc_flops_shot (sc_flopinfo_t * fi, sc_flopinfo_t * snapshot)
+{
+  sc_flops_shotv (fi, snapshot, NULL);
+}
+
+void
+sc_flops_shotv (sc_flopinfo_t * fi, ...)
+{
+  sc_flopinfo_t      *snapshot;
+  va_list             ap;
+
+  sc_flops_count (fi);
+
+  va_start (ap, fi);
+  for (; (snapshot = va_arg (ap, sc_flopinfo_t *)) != NULL;) {
+    snapshot->iwtime = fi->cwtime - snapshot->cwtime;
+    snapshot->irtime = fi->crtime - snapshot->crtime;
+    snapshot->iptime = fi->cptime - snapshot->cptime;
+    snapshot->iflpops = fi->cflpops - snapshot->cflpops;
+    snapshot->mflops =
+      (float) ((double) snapshot->iflpops / 1.e6 / snapshot->irtime);
+
+    snapshot->seconds = fi->seconds;
+    snapshot->cwtime = fi->cwtime;
+    snapshot->crtime = fi->crtime;
+    snapshot->cptime = fi->cptime;
+    snapshot->cflpops = fi->cflpops;
+  }
+  va_end (ap);
+}
diff --git a/sc/src/sc_flops.h b/sc/src/sc_flops.h
new file mode 100644
index 0000000..af212ed
--- /dev/null
+++ b/sc/src/sc_flops.h
@@ -0,0 +1,108 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_FLOPS_H
+#define SC_FLOPS_H
+
+#include <sc.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef struct sc_flopinfo
+{
+  double              seconds;  /* current time from sc_MPI_Wtime */
+
+  /* these variables measure onward from from sc_flops_start */
+  double              cwtime;   /* cumulative wall time */
+  float               crtime;   /* cumulative real time */
+  float               cptime;   /* cumulative process time */
+  long long           cflpops;  /* cumulative floating point operations */
+
+  /* measure since sc_flops_start or the previous sc_flops_count */
+  double              iwtime;   /* interval wall time */
+  float               irtime;   /* interval real time */
+  float               iptime;   /* interval process time */
+  long long           iflpops;  /* interval floating point operations */
+  float               mflops;   /* MFlop/s rate in this interval */
+
+  /* without SC_PAPI only seconds, ?wtime and ?rtime are meaningful */
+}
+sc_flopinfo_t;
+
+/**
+ * Calls PAPI_flops.  Aborts on PAPI error.
+ * The first call sets up the performance counters.
+ * Subsequent calls return cumulative real and process times,
+ * cumulative floating point operations and the flop rate since the last call.
+ */
+void                sc_flops_papi (float *rtime, float *ptime,
+                                   long long *flpops, float *mflops);
+
+/**
+ * Prepare sc_flopinfo_t structure and start flop counters.
+ * Must only be called once during the program run.
+ * This function calls sc_flops_papi.
+ *
+ * \param [out] fi  Members will be initialized.
+ */
+void                sc_flops_start (sc_flopinfo_t * fi);
+
+/**
+ * Update sc_flopinfo_t structure with current measurement.
+ * Must only be called after sc_flops_start.
+ * Can be called any number of times.
+ * This function calls sc_flops_papi.
+ *
+ * \param [in,out] fi   Members will be updated.
+ */
+void                sc_flops_count (sc_flopinfo_t * fi);
+
+/**
+ * Call sc_flops_count (fi) and copies fi into snapshot.
+ *
+ * \param [in,out] fi       Members will be updated.
+ * \param [out] snapshot    On output is a copy of fi.
+ */
+void                sc_flops_snap (sc_flopinfo_t * fi,
+                                   sc_flopinfo_t * snapshot);
+
+/**
+ * Call sc_flops_count (fi) and override snapshot interval timings
+ * with the differences since the previous call to sc_flops_snap.
+ * The interval mflop rate is computed by iflpops / 1e6 / irtime.
+ * The cumulative timings in snapshot are copied form fi.
+ *
+ * \param [in,out] fi       Members will be updated.
+ * \param [in,out] snapshot Interval timings measured since sc_flops_snap.
+ */
+void                sc_flops_shot (sc_flopinfo_t * fi,
+                                   sc_flopinfo_t * snapshot);
+
+/**
+ * Call sc_flops_count (fi) and work on all arguments in the list
+ * of type sc_flopinfo_t * as in sc_flops_shot.  Last argument must be NULL.
+ */
+void                sc_flops_shotv (sc_flopinfo_t * fi, ...);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_FLOPS_H */
diff --git a/sc/src/sc_functions.c b/sc/src/sc_functions.c
new file mode 100644
index 0000000..6c31085
--- /dev/null
+++ b/sc/src/sc_functions.c
@@ -0,0 +1,197 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_functions.h>
+#include <time.h>
+
+double
+sc_function1_invert (sc_function1_t func, void *data,
+                     double x_low, double x_high, double y, double rtol)
+{
+  const int           k_max = 100;
+  int                 k;
+  double              x, sign;
+  double              y_target, y_low, y_high, y_tol;
+
+  SC_ASSERT (x_low < x_high && rtol > 0.);
+
+  y_target = y;
+  if (func == NULL)
+    return y_target;
+
+  y_low = func (x_low, data);
+  y_high = func (x_high, data);
+  y_tol = rtol * fabs (y_high - y_low);
+  sign = (y_low <= y_high) ? 1. : -1.;
+
+  SC_ASSERT ((sign > 0. && y_low <= y_target && y_target <= y_high) ||
+             (sign < 0. && y_high <= y_target && y_target <= y_low));
+
+  for (k = 0; k < k_max; ++k) {
+    x = x_low + (x_high - x_low) * (y_target - y_low) / (y_high - y_low);
+    if (x <= x_low) {
+      return x_low;
+    }
+    if (x >= x_high) {
+      return x_high;
+    }
+
+    y = func (x, data);
+    if (sign * (y - y_target) < -y_tol) {
+      x_low = x;
+      y_low = y;
+    }
+    else if (sign * (y - y_target) > y_tol) {
+      x_high = x;
+      y_high = y;
+    }
+    else
+      return x;
+  }
+  SC_ABORTF ("sc_function1_invert did not converge after %d iterations", k);
+}
+
+void
+sc_srand (unsigned int seed)
+{
+  int                 mpiret;
+  int                 mpirank;
+
+  mpiret = sc_MPI_Comm_rank (sc_MPI_COMM_WORLD, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  /* mpirank + seed * large_prime */
+  srand ((unsigned int) mpirank + seed * 393919);
+}
+
+void
+sc_srand_time ()
+{
+  int                 mpiret;
+  int                 mpirank;
+
+  mpiret = sc_MPI_Comm_rank (sc_MPI_COMM_WORLD, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  /* time + mpirank * small_prime */
+  srand ((unsigned int) time (NULL) + mpirank * 353);
+}
+
+double
+sc_rand_uniform (void)
+{
+  return rand () / (RAND_MAX + 1.0);
+}
+
+double
+sc_rand_normal (void)
+{
+  double              u, v, s;
+
+  do {
+    u = 2.0 * (sc_rand_uniform () - 0.5);       /* uniform on [-1,1) */
+    v = 2.0 * (sc_rand_uniform () - 0.5);       /* uniform on [-1,1) */
+    s = u * u + v * v;
+  } while (s > 1.0 || s <= 0.0);
+
+  s = sqrt (-2.0 * log (s) / s);
+
+  return u * s;
+}
+
+double
+sc_zero3 (double x, double y, double z, void *data)
+{
+  return 0.;
+}
+
+double
+sc_one3 (double x, double y, double z, void *data)
+{
+  return 1.;
+}
+
+double
+sc_two3 (double x, double y, double z, void *data)
+{
+  return 2.;
+}
+
+double
+sc_ten3 (double x, double y, double z, void *data)
+{
+  return 10.;
+}
+
+double
+sc_constant3 (double x, double y, double z, void *data)
+{
+  return *(double *) data;
+}
+
+double
+sc_x3 (double x, double y, double z, void *data)
+{
+  return x;
+}
+
+double
+sc_y3 (double x, double y, double z, void *data)
+{
+  return y;
+}
+
+double
+sc_z3 (double x, double y, double z, void *data)
+{
+  return z;
+}
+
+double
+sc_sum3 (double x, double y, double z, void *data)
+{
+  sc_function3_meta_t *meta = (sc_function3_meta_t *) data;
+
+  SC_ASSERT (meta != NULL);
+  return meta->f1 (x, y, z, meta->data) +
+    ((meta->f2 != NULL) ? meta->f2 (x, y, z, meta->data) : meta->parameter2);
+}
+
+double
+sc_product3 (double x, double y, double z, void *data)
+{
+  sc_function3_meta_t *meta = (sc_function3_meta_t *) data;
+
+  SC_ASSERT (meta != NULL);
+  return meta->f1 (x, y, z, meta->data) *
+    ((meta->f2 != NULL) ? meta->f2 (x, y, z, meta->data) : meta->parameter2);
+}
+
+double
+sc_tensor3 (double x, double y, double z, void *data)
+{
+  sc_function3_meta_t *meta = (sc_function3_meta_t *) data;
+
+  SC_ASSERT (meta != NULL);
+  return meta->f1 (x, y, z, meta->data) *
+    meta->f2 (x, y, z, meta->data) * meta->f3 (x, y, z, meta->data);
+}
diff --git a/sc/src/sc_functions.h b/sc/src/sc_functions.h
new file mode 100644
index 0000000..d147f96
--- /dev/null
+++ b/sc/src/sc_functions.h
@@ -0,0 +1,106 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_FUNCTIONS_H
+#define SC_FUNCTIONS_H
+
+#include <sc.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef double      (*sc_function1_t) (double x, void *data);
+
+typedef double      (*sc_function3_t) (double x, double y, double z,
+                                       void *data);
+
+/*
+ * this structure is used as data element for the meta functions.
+ * for _sum and _product:
+ * f1 needs to be a valid function.
+ * f2 can be a function, then it is used,
+ *    or NULL, in which case parameter2 is used.
+ * for _tensor: f1, f2, f3 need to be valid functions.
+ */
+typedef struct sc_function3_meta
+{
+  sc_function3_t      f1;
+  sc_function3_t      f2;
+  double              parameter2;
+  sc_function3_t      f3;
+  void               *data;
+}
+sc_function3_meta_t;
+
+/* Evaluate the inverse function with regula falsi: x = func^{-1}(y) */
+double              sc_function1_invert (sc_function1_t func, void *data,
+                                         double x_low, double x_high,
+                                         double y, double rtol);
+
+/** Seed the random number generator differently on each process.
+ * Seeds each process with seed and mpirank from sc_MPI_COMM_WORLD.
+ *    ( mpirank + seed * large_prime )
+ *
+ * \param [in] seed Seed for random number generator, calls srand ().
+ */
+void                sc_srand (unsigned int seed);
+
+/** Seed the random number generator differently on each process.
+ * Seeds each process with time and mpirank from sc_MPI_COMM_WORLD.
+ *    ( time + mpirank * small_prime )
+ */
+void                sc_srand_time ();
+
+/** Sample a uniform value from [0,1) via rand ().
+ *
+ * \return    randum number from uniform distribution on [0,1)
+ */
+double              sc_rand_uniform (void);
+
+/** Sample a (gaussian) standard normal distribution.
+ * Implements polar form of the Box Muller transform based on rand ().
+ *
+ * \return    random number from a univariate standard normal distribution
+ */
+double              sc_rand_normal (void);
+
+/* Some basic 3D functions */
+double              sc_zero3 (double x, double y, double z, void *data);
+double              sc_one3 (double x, double y, double z, void *data);
+double              sc_two3 (double x, double y, double z, void *data);
+double              sc_ten3 (double x, double y, double z, void *data);
+
+/**
+ * \param data   needs to be *double with the value of the constant.
+ */
+double              sc_constant3 (double x, double y, double z, void *data);
+
+double              sc_x3 (double x, double y, double z, void *data);
+double              sc_y3 (double x, double y, double z, void *data);
+double              sc_z3 (double x, double y, double z, void *data);
+
+double              sc_sum3 (double x, double y, double z, void *data);
+double              sc_product3 (double x, double y, double z, void *data);
+double              sc_tensor3 (double x, double y, double z, void *data);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_FUNCTIONS_H */
diff --git a/sc/src/sc_getopt.c b/sc/src/sc_getopt.c
new file mode 100644
index 0000000..b5590ef
--- /dev/null
+++ b/sc/src/sc_getopt.c
@@ -0,0 +1,1112 @@
+/* *INDENT-OFF* */
+
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to drepper at gnu.org
+   before changing it!
+   Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002,2003,2004
+   Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* renamed from glibc 2.7 to sc_getopt.c and modified */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#include <sc.h>
+
+#ifdef SC_PROVIDE_GETOPT
+#include "sc_builtin/getopt.h"
+#include "sc_builtin/getopt_int.h"
+
+/* kill gettext */
+#define _(msgid) msgid
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* 1003.2 says this must be 1 before any call.  */
+int optind = 1;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Keep a global copy of all internal members of getopt_data.  */
+
+static struct _getopt_data getopt_data;
+
+
+#ifndef __GNU_LIBRARY__
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+#ifndef getenv
+extern char *getenv ();
+#endif
+
+#endif /* not __GNU_LIBRARY__ */
+
+#ifdef _LIBC
+/* Stored original parameters.
+   XXX This is no good solution.  We should rather copy the args so
+   that we can compare them later.  But we must not use malloc(3).  */
+extern int __libc_argc;
+extern char **__libc_argv;
+
+/* Bash 2.0 gives us an environment variable containing flags
+   indicating ARGV elements that should not be considered arguments.  */
+
+# ifdef USE_NONOPTION_FLAGS
+/* Defined in getopt_init.c  */
+extern char *__getopt_nonoption_flags;
+# endif
+
+# ifdef USE_NONOPTION_FLAGS
+#  define SWAP_FLAGS(ch1, ch2) \
+  if (d->__nonoption_flags_len > 0)					      \
+    {									      \
+      char __tmp = __getopt_nonoption_flags[ch1];			      \
+      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];	      \
+      __getopt_nonoption_flags[ch2] = __tmp;				      \
+    }
+# else
+#  define SWAP_FLAGS(ch1, ch2)
+# endif
+#else	/* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif	/* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+static void
+exchange (char **argv, struct _getopt_data *d)
+{
+  int bottom = d->__first_nonopt;
+  int middle = d->__last_nonopt;
+  int top = d->optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+  /* First make sure the handling of the `__getopt_nonoption_flags'
+     string can work normally.  Our top argument must be in the range
+     of the string.  */
+  if (d->__nonoption_flags_len > 0 && top >= d->__nonoption_flags_max_len)
+    {
+      /* We must extend the array.  The user plays games with us and
+	 presents new arguments.  */
+      char *new_str = malloc (top + 1);
+      if (new_str == NULL)
+	d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0;
+      else
+	{
+	  memset (__mempcpy (new_str, __getopt_nonoption_flags,
+			     d->__nonoption_flags_max_len),
+		  '\0', top + 1 - d->__nonoption_flags_max_len);
+	  d->__nonoption_flags_max_len = top + 1;
+	  __getopt_nonoption_flags = new_str;
+	}
+    }
+#endif
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+	{
+	  /* Bottom segment is the short one.  */
+	  int len = middle - bottom;
+	  register int i;
+
+	  /* Swap it with the top part of the top segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[top - (middle - bottom) + i];
+	      argv[top - (middle - bottom) + i] = tem;
+	      SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+	    }
+	  /* Exclude the moved bottom segment from further swapping.  */
+	  top -= len;
+	}
+      else
+	{
+	  /* Top segment is the short one.  */
+	  int len = top - middle;
+	  register int i;
+
+	  /* Swap it with the bottom part of the bottom segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[middle + i];
+	      argv[middle + i] = tem;
+	      SWAP_FLAGS (bottom + i, middle + i);
+	    }
+	  /* Exclude the moved top segment from further swapping.  */
+	  bottom += len;
+	}
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  d->__first_nonopt += (d->optind - d->__last_nonopt);
+  d->__last_nonopt = d->optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+static const char *
+_getopt_initialize (int argc, char *const *argv, const char *optstring,
+		    struct _getopt_data *d)
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  d->__first_nonopt = d->__last_nonopt = d->optind;
+
+  d->__nextchar = NULL;
+
+  d->__posixly_correct = !!getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      d->__ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      d->__ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (d->__posixly_correct)
+    d->__ordering = REQUIRE_ORDER;
+  else
+    d->__ordering = PERMUTE;
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+  if (!d->__posixly_correct
+      && argc == __libc_argc && argv == __libc_argv)
+    {
+      if (d->__nonoption_flags_max_len == 0)
+	{
+	  if (__getopt_nonoption_flags == NULL
+	      || __getopt_nonoption_flags[0] == '\0')
+	    d->__nonoption_flags_max_len = -1;
+	  else
+	    {
+	      const char *orig_str = __getopt_nonoption_flags;
+	      int len = d->__nonoption_flags_max_len = strlen (orig_str);
+	      if (d->__nonoption_flags_max_len < argc)
+		d->__nonoption_flags_max_len = argc;
+	      __getopt_nonoption_flags =
+		(char *) malloc (d->__nonoption_flags_max_len);
+	      if (__getopt_nonoption_flags == NULL)
+		d->__nonoption_flags_max_len = -1;
+	      else
+		memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+			'\0', d->__nonoption_flags_max_len - len);
+	    }
+	}
+      d->__nonoption_flags_len = d->__nonoption_flags_max_len;
+    }
+  else
+    d->__nonoption_flags_len = 0;
+#endif
+
+  return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns -1.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal_r (int argc, char *const *argv, const char *optstring,
+		    const struct option *longopts, int *longind,
+		    int long_only, struct _getopt_data *d)
+{
+  int print_errors = d->opterr;
+  if (optstring[0] == ':')
+    print_errors = 0;
+
+  if (argc < 1)
+    return -1;
+
+  d->optarg = NULL;
+
+  if (d->optind == 0 || !d->__initialized)
+    {
+      if (d->optind == 0)
+	d->optind = 1;	/* Don't scan ARGV[0], the program name.  */
+      optstring = _getopt_initialize (argc, argv, optstring, d);
+      d->__initialized = 1;
+    }
+
+  /* Test whether ARGV[optind] points to a non-option argument.
+     Either it does not have option syntax, or there is an environment flag
+     from the shell indicating it is not an option.  The later information
+     is only used when the used in the GNU libc.  */
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0' \
+		      || (d->optind < d->__nonoption_flags_len		      \
+			  && __getopt_nonoption_flags[d->optind] == '1'))
+#else
+# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0')
+#endif
+
+  if (d->__nextchar == NULL || *d->__nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+	 moved back by the user (who may also have changed the arguments).  */
+      if (d->__last_nonopt > d->optind)
+	d->__last_nonopt = d->optind;
+      if (d->__first_nonopt > d->optind)
+	d->__first_nonopt = d->optind;
+
+      if (d->__ordering == PERMUTE)
+	{
+	  /* If we have just processed some options following some non-options,
+	     exchange them so that the options come first.  */
+
+	  if (d->__first_nonopt != d->__last_nonopt
+	      && d->__last_nonopt != d->optind)
+	    exchange ((char **) argv, d);
+	  else if (d->__last_nonopt != d->optind)
+	    d->__first_nonopt = d->optind;
+
+	  /* Skip any additional non-options
+	     and extend the range of non-options previously skipped.  */
+
+	  while (d->optind < argc && NONOPTION_P)
+	    d->optind++;
+	  d->__last_nonopt = d->optind;
+	}
+
+      /* The special ARGV-element `--' means premature end of options.
+	 Skip it like a null option,
+	 then exchange with previous non-options as if it were an option,
+	 then skip everything else like a non-option.  */
+
+      if (d->optind != argc && !strcmp (argv[d->optind], "--"))
+	{
+	  d->optind++;
+
+	  if (d->__first_nonopt != d->__last_nonopt
+	      && d->__last_nonopt != d->optind)
+	    exchange ((char **) argv, d);
+	  else if (d->__first_nonopt == d->__last_nonopt)
+	    d->__first_nonopt = d->optind;
+	  d->__last_nonopt = argc;
+
+	  d->optind = argc;
+	}
+
+      /* If we have done all the ARGV-elements, stop the scan
+	 and back over any non-options that we skipped and permuted.  */
+
+      if (d->optind == argc)
+	{
+	  /* Set the next-arg-index to point at the non-options
+	     that we previously skipped, so the caller will digest them.  */
+	  if (d->__first_nonopt != d->__last_nonopt)
+	    d->optind = d->__first_nonopt;
+	  return -1;
+	}
+
+      /* If we have come to a non-option and did not permute it,
+	 either stop the scan or describe it to the caller and pass it by.  */
+
+      if (NONOPTION_P)
+	{
+	  if (d->__ordering == REQUIRE_ORDER)
+	    return -1;
+	  d->optarg = argv[d->optind++];
+	  return 1;
+	}
+
+      /* We have found another option-ARGV-element.
+	 Skip the initial punctuation.  */
+
+      d->__nextchar = (argv[d->optind] + 1
+		  + (longopts != NULL && argv[d->optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[d->optind][1] == '-'
+	  || (long_only && (argv[d->optind][2]
+			    || !strchr (optstring, argv[d->optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound = -1;
+      int option_index;
+
+      for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++)
+	/* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+	 or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+	if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar))
+	  {
+	    if ((unsigned int) (nameend - d->__nextchar)
+		== (unsigned int) strlen (p->name))
+	      {
+		/* Exact match found.  */
+		pfound = p;
+		indfound = option_index;
+		exact = 1;
+		break;
+	      }
+	    else if (pfound == NULL)
+	      {
+		/* First nonexact match found.  */
+		pfound = p;
+		indfound = option_index;
+	      }
+	    else if (long_only
+		     || pfound->has_arg != p->has_arg
+		     || pfound->flag != p->flag
+		     || pfound->val != p->val)
+	      /* Second or later nonexact match found.  */
+	      ambig = 1;
+	  }
+
+      if (ambig && !exact)
+	{
+	  if (print_errors)
+	    {
+#if defined _LIBC && defined USE_IN_LIBIO
+	      char *buf;
+
+	      if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"),
+			      argv[0], argv[d->optind]) >= 0)
+		{
+		  _IO_flockfile (stderr);
+
+		  int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+		  ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+		  __fxprintf (NULL, "%s", buf);
+
+		  ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+		  _IO_funlockfile (stderr);
+
+		  free (buf);
+		}
+#else
+	      fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+		       argv[0], argv[d->optind]);
+#endif
+	    }
+	  d->__nextchar += strlen (d->__nextchar);
+	  d->optind++;
+	  d->optopt = 0;
+	  return '?';
+	}
+
+      if (pfound != NULL)
+	{
+	  option_index = indfound;
+	  d->optind++;
+	  if (*nameend)
+	    {
+	      /* Don't test has_arg with >, because some C compilers don't
+		 allow it to be used on enums.  */
+	      if (pfound->has_arg)
+		d->optarg = nameend + 1;
+	      else
+		{
+		  if (print_errors)
+		    {
+#if defined _LIBC && defined USE_IN_LIBIO
+		      char *buf;
+		      int n;
+#endif
+
+		      if (argv[d->optind - 1][1] == '-')
+			{
+			  /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+			  n = __asprintf (&buf, _("\
+%s: option `--%s' doesn't allow an argument\n"),
+					  argv[0], pfound->name);
+#else
+			  fprintf (stderr, _("\
+%s: option `--%s' doesn't allow an argument\n"),
+				   argv[0], pfound->name);
+#endif
+			}
+		      else
+			{
+			  /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+			  n = __asprintf (&buf, _("\
+%s: option `%c%s' doesn't allow an argument\n"),
+					  argv[0], argv[d->optind - 1][0],
+					  pfound->name);
+#else
+			  fprintf (stderr, _("\
+%s: option `%c%s' doesn't allow an argument\n"),
+				   argv[0], argv[d->optind - 1][0],
+				   pfound->name);
+#endif
+			}
+
+#if defined _LIBC && defined USE_IN_LIBIO
+		      if (n >= 0)
+			{
+			  _IO_flockfile (stderr);
+
+			  int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+			  ((_IO_FILE *) stderr)->_flags2
+			    |= _IO_FLAGS2_NOTCANCEL;
+
+			  __fxprintf (NULL, "%s", buf);
+
+			  ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+			  _IO_funlockfile (stderr);
+
+			  free (buf);
+			}
+#endif
+		    }
+
+		  d->__nextchar += strlen (d->__nextchar);
+
+		  d->optopt = pfound->val;
+		  return '?';
+		}
+	    }
+	  else if (pfound->has_arg == 1)
+	    {
+	      if (d->optind < argc)
+		d->optarg = argv[d->optind++];
+	      else
+		{
+		  if (print_errors)
+		    {
+#if defined _LIBC && defined USE_IN_LIBIO
+		      char *buf;
+
+		      if (__asprintf (&buf, _("\
+%s: option `%s' requires an argument\n"),
+				      argv[0], argv[d->optind - 1]) >= 0)
+			{
+			  _IO_flockfile (stderr);
+
+			  int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+			  ((_IO_FILE *) stderr)->_flags2
+			    |= _IO_FLAGS2_NOTCANCEL;
+
+			  __fxprintf (NULL, "%s", buf);
+
+			  ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+			  _IO_funlockfile (stderr);
+
+			  free (buf);
+			}
+#else
+		      fprintf (stderr,
+			       _("%s: option `%s' requires an argument\n"),
+			       argv[0], argv[d->optind - 1]);
+#endif
+		    }
+		  d->__nextchar += strlen (d->__nextchar);
+		  d->optopt = pfound->val;
+		  return optstring[0] == ':' ? ':' : '?';
+		}
+	    }
+	  d->__nextchar += strlen (d->__nextchar);
+	  if (longind != NULL)
+	    *longind = option_index;
+	  if (pfound->flag)
+	    {
+	      *(pfound->flag) = pfound->val;
+	      return 0;
+	    }
+	  return pfound->val;
+	}
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+	 or the option starts with '--' or is not a valid short
+	 option, then it's an error.
+	 Otherwise interpret it as a short option.  */
+      if (!long_only || argv[d->optind][1] == '-'
+	  || strchr (optstring, *d->__nextchar) == NULL)
+	{
+	  if (print_errors)
+	    {
+#if defined _LIBC && defined USE_IN_LIBIO
+	      char *buf;
+	      int n;
+#endif
+
+	      if (argv[d->optind][1] == '-')
+		{
+		  /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+		  n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"),
+				  argv[0], d->__nextchar);
+#else
+		  fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+			   argv[0], d->__nextchar);
+#endif
+		}
+	      else
+		{
+		  /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+		  n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"),
+				  argv[0], argv[d->optind][0], d->__nextchar);
+#else
+		  fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+			   argv[0], argv[d->optind][0], d->__nextchar);
+#endif
+		}
+
+#if defined _LIBC && defined USE_IN_LIBIO
+	      if (n >= 0)
+		{
+		  _IO_flockfile (stderr);
+
+		  int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+		  ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+		  __fxprintf (NULL, "%s", buf);
+
+		  ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+		  _IO_funlockfile (stderr);
+
+		  free (buf);
+		}
+#endif
+	    }
+	  d->__nextchar = (char *) "";
+	  d->optind++;
+	  d->optopt = 0;
+	  return '?';
+	}
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *d->__nextchar++;
+    char *temp = strchr (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*d->__nextchar == '\0')
+      ++d->optind;
+
+    if (temp == NULL || c == ':')
+      {
+	if (print_errors)
+	  {
+#if defined _LIBC && defined USE_IN_LIBIO
+	      char *buf;
+	      int n;
+#endif
+
+	    if (d->__posixly_correct)
+	      {
+		/* 1003.2 specifies the format of this message.  */
+#if defined _LIBC && defined USE_IN_LIBIO
+		n = __asprintf (&buf, _("%s: illegal option -- %c\n"),
+				argv[0], c);
+#else
+		fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
+#endif
+	      }
+	    else
+	      {
+#if defined _LIBC && defined USE_IN_LIBIO
+		n = __asprintf (&buf, _("%s: invalid option -- %c\n"),
+				argv[0], c);
+#else
+		fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
+#endif
+	      }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+	    if (n >= 0)
+	      {
+		_IO_flockfile (stderr);
+
+		int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+		((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+		__fxprintf (NULL, "%s", buf);
+
+		((_IO_FILE *) stderr)->_flags2 = old_flags2;
+		_IO_funlockfile (stderr);
+
+		free (buf);
+	      }
+#endif
+	  }
+	d->optopt = c;
+	return '?';
+      }
+    /* Convenience. Treat POSIX -W foo same as long option --foo */
+    if (temp[0] == 'W' && temp[1] == ';')
+      {
+	char *nameend;
+	const struct option *p;
+	const struct option *pfound = NULL;
+	int exact = 0;
+	int ambig = 0;
+	int indfound = 0;
+	int option_index;
+
+	/* This is an option that requires an argument.  */
+	if (*d->__nextchar != '\0')
+	  {
+	    d->optarg = d->__nextchar;
+	    /* If we end this ARGV-element by taking the rest as an arg,
+	       we must advance to the next element now.  */
+	    d->optind++;
+	  }
+	else if (d->optind == argc)
+	  {
+	    if (print_errors)
+	      {
+		/* 1003.2 specifies the format of this message.  */
+#if defined _LIBC && defined USE_IN_LIBIO
+		char *buf;
+
+		if (__asprintf (&buf,
+				_("%s: option requires an argument -- %c\n"),
+				argv[0], c) >= 0)
+		  {
+		    _IO_flockfile (stderr);
+
+		    int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+		    ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+		    __fxprintf (NULL, "%s", buf);
+
+		    ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+		    _IO_funlockfile (stderr);
+
+		    free (buf);
+		  }
+#else
+		fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+			 argv[0], c);
+#endif
+	      }
+	    d->optopt = c;
+	    if (optstring[0] == ':')
+	      c = ':';
+	    else
+	      c = '?';
+	    return c;
+	  }
+	else
+	  /* We already incremented `d->optind' once;
+	     increment it again when taking next ARGV-elt as argument.  */
+	  d->optarg = argv[d->optind++];
+
+	/* optarg is now the argument, see if it's in the
+	   table of longopts.  */
+
+	for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '=';
+	     nameend++)
+	  /* Do nothing.  */ ;
+
+	/* Test all long options for either exact match
+	   or abbreviated matches.  */
+	for (p = longopts, option_index = 0; p->name; p++, option_index++)
+	  if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar))
+	    {
+	      if ((unsigned int) (nameend - d->__nextchar) == strlen (p->name))
+		{
+		  /* Exact match found.  */
+		  pfound = p;
+		  indfound = option_index;
+		  exact = 1;
+		  break;
+		}
+	      else if (pfound == NULL)
+		{
+		  /* First nonexact match found.  */
+		  pfound = p;
+		  indfound = option_index;
+		}
+	      else
+		/* Second or later nonexact match found.  */
+		ambig = 1;
+	    }
+	if (ambig && !exact)
+	  {
+	    if (print_errors)
+	      {
+#if defined _LIBC && defined USE_IN_LIBIO
+		char *buf;
+
+		if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"),
+				argv[0], argv[d->optind]) >= 0)
+		  {
+		    _IO_flockfile (stderr);
+
+		    int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+		    ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+		    __fxprintf (NULL, "%s", buf);
+
+		    ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+		    _IO_funlockfile (stderr);
+
+		    free (buf);
+		  }
+#else
+		fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+			 argv[0], argv[d->optind]);
+#endif
+	      }
+	    d->__nextchar += strlen (d->__nextchar);
+	    d->optind++;
+	    return '?';
+	  }
+	if (pfound != NULL)
+	  {
+	    option_index = indfound;
+	    if (*nameend)
+	      {
+		/* Don't test has_arg with >, because some C compilers don't
+		   allow it to be used on enums.  */
+		if (pfound->has_arg)
+		  d->optarg = nameend + 1;
+		else
+		  {
+		    if (print_errors)
+		      {
+#if defined _LIBC && defined USE_IN_LIBIO
+			char *buf;
+
+			if (__asprintf (&buf, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+					argv[0], pfound->name) >= 0)
+			  {
+			    _IO_flockfile (stderr);
+
+			    int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+			    ((_IO_FILE *) stderr)->_flags2
+			      |= _IO_FLAGS2_NOTCANCEL;
+
+			    __fxprintf (NULL, "%s", buf);
+
+			    ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+			    _IO_funlockfile (stderr);
+
+			    free (buf);
+			  }
+#else
+			fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+				 argv[0], pfound->name);
+#endif
+		      }
+
+		    d->__nextchar += strlen (d->__nextchar);
+		    return '?';
+		  }
+	      }
+	    else if (pfound->has_arg == 1)
+	      {
+		if (d->optind < argc)
+		  d->optarg = argv[d->optind++];
+		else
+		  {
+		    if (print_errors)
+		      {
+#if defined _LIBC && defined USE_IN_LIBIO
+			char *buf;
+
+			if (__asprintf (&buf, _("\
+%s: option `%s' requires an argument\n"),
+					argv[0], argv[d->optind - 1]) >= 0)
+			  {
+			    _IO_flockfile (stderr);
+
+			    int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+			    ((_IO_FILE *) stderr)->_flags2
+			      |= _IO_FLAGS2_NOTCANCEL;
+
+			    __fxprintf (NULL, "%s", buf);
+
+			    ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+			    _IO_funlockfile (stderr);
+
+			    free (buf);
+			  }
+#else
+			fprintf (stderr,
+				 _("%s: option `%s' requires an argument\n"),
+				 argv[0], argv[d->optind - 1]);
+#endif
+		      }
+		    d->__nextchar += strlen (d->__nextchar);
+		    return optstring[0] == ':' ? ':' : '?';
+		  }
+	      }
+	    d->__nextchar += strlen (d->__nextchar);
+	    if (longind != NULL)
+	      *longind = option_index;
+	    if (pfound->flag)
+	      {
+		*(pfound->flag) = pfound->val;
+		return 0;
+	      }
+	    return pfound->val;
+	  }
+	  d->__nextchar = NULL;
+	  return 'W';	/* Let the application handle it.   */
+      }
+    if (temp[1] == ':')
+      {
+	if (temp[2] == ':')
+	  {
+	    /* This is an option that accepts an argument optionally.  */
+	    if (*d->__nextchar != '\0')
+	      {
+		d->optarg = d->__nextchar;
+		d->optind++;
+	      }
+	    else
+	      d->optarg = NULL;
+	    d->__nextchar = NULL;
+	  }
+	else
+	  {
+	    /* This is an option that requires an argument.  */
+	    if (*d->__nextchar != '\0')
+	      {
+		d->optarg = d->__nextchar;
+		/* If we end this ARGV-element by taking the rest as an arg,
+		   we must advance to the next element now.  */
+		d->optind++;
+	      }
+	    else if (d->optind == argc)
+	      {
+		if (print_errors)
+		  {
+		    /* 1003.2 specifies the format of this message.  */
+#if defined _LIBC && defined USE_IN_LIBIO
+		    char *buf;
+
+		    if (__asprintf (&buf, _("\
+%s: option requires an argument -- %c\n"),
+				    argv[0], c) >= 0)
+		      {
+			_IO_flockfile (stderr);
+
+			int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+			((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+			__fxprintf (NULL, "%s", buf);
+
+			((_IO_FILE *) stderr)->_flags2 = old_flags2;
+			_IO_funlockfile (stderr);
+
+			free (buf);
+		      }
+#else
+		    fprintf (stderr,
+			     _("%s: option requires an argument -- %c\n"),
+			     argv[0], c);
+#endif
+		  }
+		d->optopt = c;
+		if (optstring[0] == ':')
+		  c = ':';
+		else
+		  c = '?';
+	      }
+	    else
+	      /* We already incremented `optind' once;
+		 increment it again when taking next ARGV-elt as argument.  */
+	      d->optarg = argv[d->optind++];
+	    d->__nextchar = NULL;
+	  }
+      }
+    return c;
+  }
+}
+
+int
+_getopt_internal (int argc, char *const *argv, const char *optstring,
+		  const struct option *longopts, int *longind, int long_only)
+{
+  int result;
+
+  getopt_data.optind = optind;
+  getopt_data.opterr = opterr;
+
+  result = _getopt_internal_r (argc, argv, optstring, longopts,
+			       longind, long_only, &getopt_data);
+
+  optind = getopt_data.optind;
+  optarg = getopt_data.optarg;
+  optopt = getopt_data.optopt;
+
+  return result;
+}
+
+int
+getopt (int argc, char *const *argv, const char *optstring)
+{
+  return _getopt_internal (argc, argv, optstring,
+			   (const struct option *) 0,
+			   (int *) 0,
+			   0);
+}
+
+#endif /* SC_PROVIDE_GETOPT */
+
+/* *INDENT-ON* */
diff --git a/sc/src/sc_getopt.h b/sc/src/sc_getopt.h
new file mode 100644
index 0000000..5e1ac38
--- /dev/null
+++ b/sc/src/sc_getopt.h
@@ -0,0 +1,37 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_GETOPT_H
+#define SC_GETOPT_H
+
+#include <sc.h>
+
+#ifdef SC_PROVIDE_GETOPT
+#ifdef _GETOPT_H
+#error "getopt.h is included.  Please #include sc.h first."
+#endif
+#include "sc_builtin/getopt.h"
+#else
+#include <getopt.h>
+#endif
+
+#endif /* !SC_GETOPT_H */
diff --git a/sc/src/sc_getopt1.c b/sc/src/sc_getopt1.c
new file mode 100644
index 0000000..b1333ef
--- /dev/null
+++ b/sc/src/sc_getopt1.c
@@ -0,0 +1,70 @@
+/* *INDENT-OFF* */
+
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98,2004
+     Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* renamed from glibc 2.7 to sc_getopt1.c and modified */
+
+#include <sc.h>
+
+#ifdef SC_PROVIDE_GETOPT
+#include "sc_builtin/getopt.h"
+#include "sc_builtin/getopt_int.h"
+
+int
+getopt_long (int argc, char *const *argv, const char *options,
+	     const struct option *long_options, int *opt_index)
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+int
+_getopt_long_r (int argc, char *const *argv, const char *options,
+		const struct option *long_options, int *opt_index,
+		struct _getopt_data *d)
+{
+  return _getopt_internal_r (argc, argv, options, long_options, opt_index,
+			     0, d);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (int argc, char *const *argv, const char *options,
+		  const struct option *long_options, int *opt_index)
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+int
+_getopt_long_only_r (int argc, char *const *argv, const char *options,
+		     const struct option *long_options, int *opt_index,
+		     struct _getopt_data *d)
+{
+  return _getopt_internal_r (argc, argv, options, long_options, opt_index,
+			     1, d);
+}
+
+#endif /* SC_PROVIDE_GETOPT */
+
+/* *INDENT-ON* */
diff --git a/sc/src/sc_io.c b/sc/src/sc_io.c
new file mode 100644
index 0000000..831e16c
--- /dev/null
+++ b/sc/src/sc_io.c
@@ -0,0 +1,509 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_io.h>
+#include <libb64.h>
+#ifdef SC_HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+sc_io_sink_t       *
+sc_io_sink_new (sc_io_type_t iotype, sc_io_mode_t mode,
+                sc_io_encode_t encode, ...)
+{
+  sc_io_sink_t       *sink;
+  va_list             ap;
+
+  SC_ASSERT (0 <= iotype && iotype < SC_IO_TYPE_LAST);
+  SC_ASSERT (0 <= mode && mode < SC_IO_MODE_LAST);
+  SC_ASSERT (0 <= encode && encode < SC_IO_ENCODE_LAST);
+
+  sink = SC_ALLOC_ZERO (sc_io_sink_t, 1);
+  sink->iotype = iotype;
+  sink->mode = mode;
+  sink->encode = encode;
+
+  va_start (ap, encode);
+  if (iotype == SC_IO_TYPE_BUFFER) {
+    sink->buffer = va_arg (ap, sc_array_t *);
+    if (sink->mode == SC_IO_MODE_WRITE) {
+      sc_array_resize (sink->buffer, 0);
+    }
+  }
+  else if (iotype == SC_IO_TYPE_FILENAME) {
+    const char         *filename = va_arg (ap, const char *);
+
+    sink->file = fopen (filename,
+                        sink->mode == SC_IO_MODE_WRITE ? "wb" : "ab");
+    if (sink->file == NULL) {
+      SC_FREE (sink);
+      return NULL;
+    }
+  }
+  else if (iotype == SC_IO_TYPE_FILEFILE) {
+    sink->file = va_arg (ap, FILE *);
+    if (ferror (sink->file)) {
+      SC_FREE (sink);
+      return NULL;
+    }
+  }
+  else {
+    SC_ABORT_NOT_REACHED ();
+  }
+  va_end (ap);
+
+  return sink;
+}
+
+int
+sc_io_sink_destroy (sc_io_sink_t * sink)
+{
+  int                 retval;
+
+  /* The error value SC_IO_ERROR_AGAIN is turned into FATAL */
+  retval = sc_io_sink_complete (sink, NULL, NULL);
+  if (sink->iotype == SC_IO_TYPE_FILENAME) {
+    SC_ASSERT (sink->file != NULL);
+
+    /* Attempt close even on complete error */
+    retval = fclose (sink->file) || retval;
+  }
+  SC_FREE (sink);
+
+  return retval ? SC_IO_ERROR_FATAL : SC_IO_ERROR_NONE;
+}
+
+int
+sc_io_sink_write (sc_io_sink_t * sink, const void *data, size_t bytes_avail)
+{
+  size_t              bytes_out;
+
+  bytes_out = 0;
+
+  if (sink->iotype == SC_IO_TYPE_BUFFER) {
+    size_t              elem_size, new_count;
+
+    SC_ASSERT (sink->buffer != NULL);
+    elem_size = sink->buffer->elem_size;
+    new_count =
+      (sink->buffer_bytes + bytes_avail + elem_size - 1) / elem_size;
+    sc_array_resize (sink->buffer, new_count);
+    /* For a view sufficient size is asserted only in debug mode. */
+    if (new_count * elem_size > SC_ARRAY_BYTE_ALLOC (sink->buffer)) {
+      return SC_IO_ERROR_FATAL;
+    }
+
+    memcpy (sink->buffer->array + sink->buffer_bytes, data, bytes_avail);
+    sink->buffer_bytes += bytes_avail;
+    bytes_out = bytes_avail;
+  }
+  else if (sink->iotype == SC_IO_TYPE_FILENAME ||
+           sink->iotype == SC_IO_TYPE_FILEFILE) {
+    SC_ASSERT (sink->file != NULL);
+    bytes_out = fwrite (data, 1, bytes_avail, sink->file);
+    if (bytes_out != bytes_avail) {
+      return SC_IO_ERROR_FATAL;
+    }
+  }
+
+  sink->bytes_in += bytes_avail;
+  sink->bytes_out += bytes_out;
+
+  return SC_IO_ERROR_NONE;
+}
+
+int
+sc_io_sink_complete (sc_io_sink_t * sink,
+                     size_t * bytes_in, size_t * bytes_out)
+{
+  int                 retval;
+
+  retval = 0;
+  if (sink->iotype == SC_IO_TYPE_BUFFER) {
+    SC_ASSERT (sink->buffer != NULL);
+    if (sink->buffer_bytes % sink->buffer->elem_size != 0) {
+      return SC_IO_ERROR_AGAIN;
+    }
+  }
+  else if (sink->iotype == SC_IO_TYPE_FILENAME ||
+           sink->iotype == SC_IO_TYPE_FILEFILE) {
+    SC_ASSERT (sink->file != NULL);
+    retval = fflush (sink->file);
+  }
+  if (retval) {
+    return SC_IO_ERROR_FATAL;
+  }
+
+  if (bytes_in != NULL) {
+    *bytes_in = sink->bytes_in;
+  }
+  if (bytes_out != NULL) {
+    *bytes_out = sink->bytes_out;
+  }
+  sink->bytes_in = sink->bytes_out = 0;
+
+  return SC_IO_ERROR_NONE;
+}
+
+sc_io_source_t     *
+sc_io_source_new (sc_io_type_t iotype, sc_io_encode_t encode, ...)
+{
+  sc_io_source_t     *source;
+  va_list             ap;
+
+  SC_ASSERT (0 <= iotype && iotype < SC_IO_TYPE_LAST);
+  SC_ASSERT (0 <= encode && encode < SC_IO_ENCODE_LAST);
+
+  source = SC_ALLOC_ZERO (sc_io_source_t, 1);
+  source->iotype = iotype;
+  source->encode = encode;
+
+  va_start (ap, encode);
+  if (iotype == SC_IO_TYPE_BUFFER) {
+    source->buffer = va_arg (ap, sc_array_t *);
+  }
+  else if (iotype == SC_IO_TYPE_FILENAME) {
+    const char         *filename = va_arg (ap, const char *);
+
+    source->file = fopen (filename, "rb");
+    if (source->file == NULL) {
+      SC_FREE (source);
+      return NULL;
+    }
+  }
+  else if (iotype == SC_IO_TYPE_FILEFILE) {
+    source->file = va_arg (ap, FILE *);
+    if (ferror (source->file)) {
+      SC_FREE (source);
+      return NULL;
+    }
+  }
+  else {
+    SC_ABORT_NOT_REACHED ();
+  }
+  va_end (ap);
+
+  return source;
+}
+
+int
+sc_io_source_destroy (sc_io_source_t * source)
+{
+  int                 retval;
+
+  /* The error value SC_IO_ERROR_AGAIN is turned into FATAL */
+  retval = sc_io_source_complete (source, NULL, NULL);
+  if (source->iotype == SC_IO_TYPE_FILENAME) {
+    SC_ASSERT (source->file != NULL);
+
+    /* Attempt close even on complete error */
+    retval = fclose (source->file) || retval;
+  }
+  SC_FREE (source);
+
+  return retval ? SC_IO_ERROR_FATAL : SC_IO_ERROR_NONE;
+}
+
+int
+sc_io_source_read (sc_io_source_t * source, void *data,
+                   size_t bytes_avail, size_t * bytes_out)
+{
+  int                 retval;
+  size_t              bbytes_out;
+
+  retval = 0;
+  bbytes_out = 0;
+
+  if (source->iotype == SC_IO_TYPE_BUFFER) {
+    SC_ASSERT (source->buffer != NULL);
+    bbytes_out = SC_ARRAY_BYTE_ALLOC (source->buffer);
+    SC_ASSERT (bbytes_out >= source->buffer_bytes);
+    bbytes_out -= source->buffer_bytes;
+    bbytes_out = SC_MIN (bbytes_out, bytes_avail);
+
+    if (data != NULL) {
+      memcpy (data, source->buffer->array + source->buffer_bytes, bbytes_out);
+    }
+    source->buffer_bytes += bbytes_out;
+  }
+  else if (source->iotype == SC_IO_TYPE_FILENAME ||
+           source->iotype == SC_IO_TYPE_FILEFILE) {
+    SC_ASSERT (source->file != NULL);
+    if (data != NULL) {
+      bbytes_out = fread (data, 1, bytes_avail, source->file);
+      if (bbytes_out < bytes_avail) {
+        retval = !feof (source->file) || ferror (source->file);
+      }
+    }
+    else {
+      retval = fseek (source->file, (long) bytes_avail, SEEK_CUR);
+      bbytes_out = bytes_avail;
+    }
+  }
+  if (retval) {
+    return SC_IO_ERROR_FATAL;
+  }
+  if (bytes_out == NULL && bbytes_out < bytes_avail) {
+    return SC_IO_ERROR_FATAL;
+  }
+
+  if (bytes_out != NULL) {
+    *bytes_out = bbytes_out;
+  }
+  source->bytes_in += bbytes_out;
+  source->bytes_out += bbytes_out;
+
+  return SC_IO_ERROR_NONE;
+}
+
+int
+sc_io_source_complete (sc_io_source_t * source,
+                       size_t * bytes_in, size_t * bytes_out)
+{
+  if (source->iotype == SC_IO_TYPE_BUFFER) {
+    SC_ASSERT (source->buffer != NULL);
+    if (source->buffer_bytes % source->buffer->elem_size != 0) {
+      return SC_IO_ERROR_AGAIN;
+    }
+  }
+
+  if (bytes_in != NULL) {
+    *bytes_in = source->bytes_in;
+  }
+  if (bytes_out != NULL) {
+    *bytes_out = source->bytes_out;
+  }
+  source->bytes_in = source->bytes_out = 0;
+
+  return SC_IO_ERROR_NONE;
+}
+
+int
+sc_vtk_write_binary (FILE * vtkfile, char *numeric_data, size_t byte_length)
+{
+  size_t              chunks, chunksize, remaining, writenow;
+  size_t              code_length, base_length;
+  uint32_t            int_header;
+  char               *base_data;
+  base64_encodestate  encode_state;
+
+  /* VTK format used 32bit header info */
+  SC_ASSERT (byte_length <= (size_t) UINT32_MAX);
+
+  /* This value may be changed although this is not tested with VTK */
+  chunksize = (size_t) 1 << 15; /* 32768 */
+  int_header = (uint32_t) byte_length;
+
+  /* Allocate sufficient memory for base64 encoder */
+  code_length = 2 * SC_MAX (chunksize, sizeof (int_header));
+  code_length = SC_MAX (code_length, 4) + 1;
+  base_data = SC_ALLOC (char, code_length);
+
+  base64_init_encodestate (&encode_state);
+  base_length =
+    base64_encode_block ((char *) &int_header, sizeof (int_header), base_data,
+                         &encode_state);
+  SC_ASSERT (base_length < code_length);
+  base_data[base_length] = '\0';
+  (void) fwrite (base_data, 1, base_length, vtkfile);
+
+  chunks = 0;
+  remaining = byte_length;
+  while (remaining > 0) {
+    writenow = SC_MIN (remaining, chunksize);
+    base_length = base64_encode_block (numeric_data + chunks * chunksize,
+                                       writenow, base_data, &encode_state);
+    SC_ASSERT (base_length < code_length);
+    base_data[base_length] = '\0';
+    (void) fwrite (base_data, 1, base_length, vtkfile);
+    remaining -= writenow;
+    ++chunks;
+  }
+
+  base_length = base64_encode_blockend (base_data, &encode_state);
+  SC_ASSERT (base_length < code_length);
+  base_data[base_length] = '\0';
+  (void) fwrite (base_data, 1, base_length, vtkfile);
+
+  SC_FREE (base_data);
+  if (ferror (vtkfile)) {
+    return -1;
+  }
+  return 0;
+}
+
+int
+sc_vtk_write_compressed (FILE * vtkfile, char *numeric_data,
+                         size_t byte_length)
+{
+#ifdef SC_HAVE_ZLIB
+  int                 retval, fseek1, fseek2;
+  size_t              iz;
+  size_t              blocksize, lastsize;
+  size_t              theblock, numregularblocks, numfullblocks;
+  size_t              header_entries, header_size;
+  size_t              code_length, base_length;
+  long                header_pos, final_pos;
+  char               *comp_data, *base_data;
+  uint32_t           *compression_header;
+  uLongf              comp_length;
+  base64_encodestate  encode_state;
+
+  /* compute block sizes */
+  blocksize = (size_t) (1 << 15);       /* 32768 */
+  lastsize = byte_length % blocksize;
+  numregularblocks = byte_length / blocksize;
+  numfullblocks = numregularblocks + (lastsize > 0 ? 1 : 0);
+  header_entries = 3 + numfullblocks;
+  header_size = header_entries * sizeof (uint32_t);
+
+  /* allocate compression and base64 arrays */
+  code_length = 2 * SC_MAX (blocksize, header_size) + 4 + 1;
+  comp_data = SC_ALLOC (char, code_length);
+  base_data = SC_ALLOC (char, code_length);
+
+  /* figure out the size of the header and write a dummy */
+  compression_header = SC_ALLOC (uint32_t, header_entries);
+  compression_header[0] = (uint32_t) numfullblocks;
+  compression_header[1] = (uint32_t) blocksize;
+  compression_header[2] = (uint32_t)
+    (lastsize > 0 || byte_length == 0 ? lastsize : blocksize);
+  for (iz = 3; iz < header_entries; ++iz) {
+    compression_header[iz] = 0;
+  }
+  base64_init_encodestate (&encode_state);
+  base_length = base64_encode_block ((char *) compression_header,
+                                     header_size, base_data, &encode_state);
+  base_length +=
+    base64_encode_blockend (base_data + base_length, &encode_state);
+  SC_ASSERT (base_length < code_length);
+  base_data[base_length] = '\0';
+  header_pos = ftell (vtkfile);
+  (void) fwrite (base_data, 1, base_length, vtkfile);
+
+  /* write the regular data blocks */
+  base64_init_encodestate (&encode_state);
+  for (theblock = 0; theblock < numregularblocks; ++theblock) {
+    comp_length = code_length;
+    retval = compress2 ((Bytef *) comp_data, &comp_length,
+                        (const Bytef *) (numeric_data + theblock * blocksize),
+                        (uLong) blocksize, Z_BEST_COMPRESSION);
+    SC_CHECK_ZLIB (retval);
+    compression_header[3 + theblock] = comp_length;
+    base_length = base64_encode_block (comp_data, comp_length,
+                                       base_data, &encode_state);
+    SC_ASSERT (base_length < code_length);
+    base_data[base_length] = '\0';
+    (void) fwrite (base_data, 1, base_length, vtkfile);
+  }
+
+  /* write odd-sized last block if necessary */
+  if (lastsize > 0) {
+    comp_length = code_length;
+    retval = compress2 ((Bytef *) comp_data, &comp_length,
+                        (const Bytef *) (numeric_data + theblock * blocksize),
+                        (uLong) lastsize, Z_BEST_COMPRESSION);
+    SC_CHECK_ZLIB (retval);
+    compression_header[3 + theblock] = comp_length;
+    base_length = base64_encode_block (comp_data, comp_length,
+                                       base_data, &encode_state);
+    SC_ASSERT (base_length < code_length);
+    base_data[base_length] = '\0';
+    (void) fwrite (base_data, 1, base_length, vtkfile);
+  }
+
+  /* write base64 end block */
+  base_length = base64_encode_blockend (base_data, &encode_state);
+  SC_ASSERT (base_length < code_length);
+  base_data[base_length] = '\0';
+  (void) fwrite (base_data, 1, base_length, vtkfile);
+
+  /* seek back, write header block, seek forward */
+  final_pos = ftell (vtkfile);
+  base64_init_encodestate (&encode_state);
+  base_length = base64_encode_block ((char *) compression_header,
+                                     header_size, base_data, &encode_state);
+  base_length +=
+    base64_encode_blockend (base_data + base_length, &encode_state);
+  SC_ASSERT (base_length < code_length);
+  base_data[base_length] = '\0';
+  fseek1 = fseek (vtkfile, header_pos, SEEK_SET);
+  (void) fwrite (base_data, 1, base_length, vtkfile);
+  fseek2 = fseek (vtkfile, final_pos, SEEK_SET);
+
+  /* clean up and return */
+  SC_FREE (compression_header);
+  SC_FREE (comp_data);
+  SC_FREE (base_data);
+  if (fseek1 != 0 || fseek2 != 0 || ferror (vtkfile)) {
+    return -1;
+  }
+#else
+  SC_ABORT ("Configure did not find a recent enough zlib.  Abort.\n");
+#endif
+
+  return 0;
+}
+
+void
+sc_fwrite (const void *ptr, size_t size, size_t nmemb, FILE * file,
+           const char *errmsg)
+{
+  size_t              nwritten;
+
+  nwritten = fwrite (ptr, size, nmemb, file);
+  SC_CHECK_ABORT (nwritten == nmemb, errmsg);
+}
+
+void
+sc_fread (void *ptr, size_t size, size_t nmemb, FILE * file,
+          const char *errmsg)
+{
+  size_t              nread;
+
+  nread = fread (ptr, size, nmemb, file);
+  SC_CHECK_ABORT (nread == nmemb, errmsg);
+}
+
+#ifdef SC_ENABLE_MPIIO
+
+void
+sc_mpi_write (MPI_File mpifile, const void *ptr, size_t zcount,
+              sc_MPI_Datatype t, const char *errmsg)
+{
+#ifdef SC_DEBUG
+  int                 icount;
+#endif
+  int                 mpiret;
+  sc_MPI_Status       mpistatus;
+
+  mpiret = MPI_File_write (mpifile, (void *) ptr,
+                           (int) zcount, t, &mpistatus);
+  SC_CHECK_ABORT (mpiret == sc_MPI_SUCCESS, errmsg);
+
+#ifdef SC_DEBUG
+  sc_MPI_Get_count (&mpistatus, t, &icount);
+  SC_CHECK_ABORT (icount == (int) zcount, errmsg);
+#endif
+}
+
+#endif
diff --git a/sc/src/sc_io.h b/sc/src/sc_io.h
new file mode 100644
index 0000000..054b0e5
--- /dev/null
+++ b/sc/src/sc_io.h
@@ -0,0 +1,266 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_IO_H
+#define SC_IO_H
+
+#include <sc.h>
+#include <sc_containers.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Error values for io.
+ */
+typedef enum
+{
+  SC_IO_ERROR_NONE,     /**< The value of zero means no error. */
+  SC_IO_ERROR_FATAL = -1,       /**< The io object is now disfunctional. */
+  SC_IO_ERROR_AGAIN = -2        /**< Another io operation may resolve it.
+                                The function just returned was a noop. */
+}
+sc_io_error_t;
+
+typedef enum
+{
+  SC_IO_MODE_WRITE,     /**< Semantics as "w" in fopen. */
+  SC_IO_MODE_APPEND,    /**< Semantics as "a" in fopen. */
+  SC_IO_MODE_LAST       /**< Invalid entry to close list */
+}
+sc_io_mode_t;
+
+typedef enum
+{
+  SC_IO_ENCODE_NONE,
+  SC_IO_ENCODE_LAST     /**< Invalid entry to close list */
+}
+sc_io_encode_t;
+
+typedef enum
+{
+  SC_IO_TYPE_BUFFER,
+  SC_IO_TYPE_FILENAME,
+  SC_IO_TYPE_FILEFILE,
+  SC_IO_TYPE_LAST       /**< Invalid entry to close list */
+}
+sc_io_type_t;
+
+typedef struct sc_io_sink
+{
+  sc_io_type_t        iotype;
+  sc_io_mode_t        mode;
+  sc_io_encode_t      encode;
+  sc_array_t         *buffer;
+  size_t              buffer_bytes;    /**< distinguish from array elems */
+  FILE               *file;
+  size_t              bytes_in;
+  size_t              bytes_out;
+}
+sc_io_sink_t;
+
+typedef struct sc_io_source
+{
+  sc_io_type_t        iotype;
+  sc_io_encode_t      encode;
+  sc_array_t         *buffer;
+  size_t              buffer_bytes;    /**< distinguish from array elems */
+  FILE               *file;
+  size_t              bytes_in;
+  size_t              bytes_out;
+}
+sc_io_source_t;
+
+/** Create a generic data sink.
+ * \param [in] iotype           Type of the sink.
+ *                              Depending on iotype, varargs must follow:
+ *                              BUFFER: sc_array_t * (existing array).
+ *                              FILENAME: const char * (name of file to open).
+ *                              FILEFILE: FILE * (file open for writing).
+ *                              These buffers are only borrowed by the sink.
+ * \param [in] mode             Mode to add data to sink.
+ *                              For type FILEFILE, data is always appended.
+ * \param [in] encode           Type of data encoding.
+ * \return                      Newly allocated sink, or NULL on error.
+ */
+sc_io_sink_t       *sc_io_sink_new (sc_io_type_t iotype,
+                                    sc_io_mode_t mode,
+                                    sc_io_encode_t encode, ...);
+
+/** Free data sink.
+ * Calls sc_io_sink_complete and discards the final counts.
+ * Errors from complete lead to SC_IO_ERROR_FATAL returned from this function.
+ * Call sc_io_sink_complete yourself if bytes_out is of interest.
+ * \param [in,out] sink         The sink object to complete and free.
+ * \return                      0 on success, nonzero on error.
+ */
+int                 sc_io_sink_destroy (sc_io_sink_t * sink);
+
+/** Write data to a sink.  Data may be buffered and sunk in a later call.
+ * The internal counters sink->bytes_in and sink->bytes_out are updated.
+ * \param [in,out] sink         The sink object to write to.
+ * \param [in] data             Data passed into sink.
+ * \param [in] bytes_avail      Number of data bytes passed in.;
+ * \return                      0 on success, nonzero on error.
+ */
+int                 sc_io_sink_write (sc_io_sink_t * sink,
+                                      const void *data, size_t bytes_avail);
+
+/** Flush all buffered output data to sink.
+ * This function may return SC_IO_ERROR_AGAIN if another write is required.
+ * Currently this may happen if BUFFER requires an integer multiple of bytes.
+ * If successful, the updated value of bytes read and written is returned
+ * in bytes_in/out, and the sink status is reset as if the sink had just
+ * been created.  In particular, the bytes counters are reset to zero.
+ * The internal state of the sink is not changed otherwise.
+ * It is legal to continue writing to the sink hereafter.
+ * The sink actions taken depend on its type.
+ * BUFFER, FILEFILE: none.
+ * FILENAME: call fclose on sink->file.
+ * \param [in,out] sink         The sink object to write to.
+ * \param [in,out] bytes_in     Bytes received since the last new or complete
+ *                              call.  May be NULL.
+ * \param [in,out] bytes_out    Bytes written since the last new or complete
+ *                              call.  May be NULL.
+ * \return                      0 if completed, nonzero on error.
+ */
+int                 sc_io_sink_complete (sc_io_sink_t * sink,
+                                         size_t * bytes_in,
+                                         size_t * bytes_out);
+
+/** Create a generic data source.
+ * \param [in] iotype           Type of the source.
+ *                              Depending on iotype, varargs must follow:
+ *                              BUFFER: sc_array_t * (existing array).
+ *                              FILENAME: const char * (name of file to open).
+ *                              FILEFILE: FILE * (file open for reading).
+ * \param [in] encode           Type of data encoding.
+ * \return                      Newly allocated source, or NULL on error.
+ */
+sc_io_source_t     *sc_io_source_new (sc_io_type_t iotype,
+                                      sc_io_encode_t encode, ...);
+
+/** Free data source.
+ * Calls sc_io_source_complete and requires it to return no error.
+ * This is to avoid discarding buffered data that has not been passed to read.
+ * \param [in,out] source       The source object to free.
+ * \return                      0 on success.  Nonzero if an error is
+ *                              encountered or is_complete returns one.
+ */
+int                 sc_io_source_destroy (sc_io_source_t * source);
+
+/** Read data from a source.
+ * The internal counters source->bytes_in and source->bytes_out are updated.
+ * Data is read until the data buffer has not enough room anymore, or source
+ * becomes empty.  It is possible that data already read internally remains
+ * in the source object for the next call.  Call sc_io_source_complete and
+ * check its return value to find out.
+ * Returns an error if bytes_out is NULL and less than bytes_avail are read.
+ * \param [in,out] source       The source object to read from.
+ * \param [in] data             Data buffer for reading from sink.
+ *                              If NULL the output data will be thrown away.
+ * \param [in] bytes_avail      Number of bytes available in data buffer.
+ * \param [in,out] bytes_out    If not NULL, byte count read into data buffer.
+ *                              Otherwise, requires to read exactly bytes_avail.
+ * \return                      0 on success, nonzero on error.
+ */
+int                 sc_io_source_read (sc_io_source_t * source,
+                                       void *data, size_t bytes_avail,
+                                       size_t * bytes_out);
+
+/** Determine whether all data buffered from source has been returned by read.
+ * If it returns SC_IO_ERROR_AGAIN, another sc_io_source_read is required.
+ * If the call returns no error, the internal counters source->bytes_in and
+ * source->bytes_out are returned to the caller if requested, and reset to 0.
+ * The internal state of the source is not changed otherwise.
+ * It is legal to continue reading from the source hereafter.
+ *
+ * \param [in,out] source       The source object to read from.
+ * \param [in,out] bytes_in     If not NULL and true is returned,
+ *                              the total size of the data sourced.
+ * \param [in,out] bytes_out    If not NULL and true is returned,
+ *                              total bytes passed out by source_read.
+ * \return                      SC_IO_ERROR_AGAIN if buffered data remaining.
+ *                              Otherwise return ERROR_NONE and reset counters.
+ */
+int                 sc_io_source_complete (sc_io_source_t * source,
+                                           size_t * bytes_in,
+                                           size_t * bytes_out);
+
+/** This function writes numeric binary data in VTK base64 encoding.
+ * \param vtkfile        Stream openened for writing.
+ * \param numeric_data   A pointer to a numeric data array.
+ * \param byte_length    The length of the data array in bytes.
+ * \return               Returns 0 on success, -1 on file error.
+ */
+int                 sc_vtk_write_binary (FILE * vtkfile, char *numeric_data,
+                                         size_t byte_length);
+
+/** This function writes numeric binary data in VTK compressed format.
+ * \param vtkfile        Stream openened for writing.
+ * \param numeric_data   A pointer to a numeric data array.
+ * \param byte_length    The length of the data array in bytes.
+ * \return               Returns 0 on success, -1 on file error.
+ */
+int                 sc_vtk_write_compressed (FILE * vtkfile,
+                                             char *numeric_data,
+                                             size_t byte_length);
+
+/** Write memory content to a file.
+ * \param [in] ptr      Data array to write to disk.
+ * \param [in] size     Size of one array member.
+ * \param [in] nmemb    Number of array members.
+ * \param [in,out] file File pointer, must be opened for writing.
+ * \param [in] errmsg   Error message passed to SC_CHECK_ABORT.
+ * \note                This function aborts on file errors.
+ */
+void                sc_fwrite (const void *ptr, size_t size,
+                               size_t nmemb, FILE * file, const char *errmsg);
+
+/** Read file content into memory.
+ * \param [out] ptr     Data array to read from disk.
+ * \param [in] size     Size of one array member.
+ * \param [in] nmemb    Number of array members.
+ * \param [in,out] file File pointer, must be opened for reading.
+ * \param [in] errmsg   Error message passed to SC_CHECK_ABORT.
+ * \note                This function aborts on file errors.
+ */
+void                sc_fread (void *ptr, size_t size,
+                              size_t nmemb, FILE * file, const char *errmsg);
+
+#ifdef SC_ENABLE_MPIIO
+
+/** Write memory content to an MPI file.
+ * \param [in,out] mpifile      MPI file object opened for writing.
+ * \param [in] ptr      Data array to write to disk.
+ * \param [in] zcount   Number of array members.
+ * \param [in] t        The MPI type for each array member.
+ * \param [in] errmsg   Error message passed to SC_CHECK_ABORT.
+ * \note                This function aborts on MPI file and count errors.
+ */
+void                sc_mpi_write (MPI_File mpifile, const void *ptr,
+                                  size_t zcount, sc_MPI_Datatype t,
+                                  const char *errmsg);
+
+#endif
+
+SC_EXTERN_C_END;
+
+#endif /* SC_IO_H */
diff --git a/sc/src/sc_keyvalue.c b/sc/src/sc_keyvalue.c
new file mode 100644
index 0000000..c458954
--- /dev/null
+++ b/sc/src/sc_keyvalue.c
@@ -0,0 +1,447 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_keyvalue.h>
+
+typedef struct sc_keyvalue_entry
+{
+  const char         *key;
+  sc_keyvalue_entry_type_t type;
+  union
+  {
+    int                 i;
+    double              g;
+    const char         *s;
+    void               *p;
+  }
+  value;
+}
+sc_keyvalue_entry_t;
+
+static unsigned
+sc_keyvalue_entry_hash (const void *v, const void *u)
+{
+  const sc_keyvalue_entry_t *ov = (const sc_keyvalue_entry_t *) v;
+
+  return sc_hash_function_string (ov->key, NULL);
+}
+
+static int
+sc_keyvalue_entry_equal (const void *v1, const void *v2, const void *u)
+{
+
+  const sc_keyvalue_entry_t *ov1 = (const sc_keyvalue_entry_t *) v1;
+  const sc_keyvalue_entry_t *ov2 = (const sc_keyvalue_entry_t *) v2;
+
+  return !strcmp (ov1->key, ov2->key);
+}
+
+sc_keyvalue_t      *
+sc_keyvalue_newv (va_list ap)
+{
+  const char         *s;
+  int                 added;
+  void              **found;
+  sc_keyvalue_t      *kv;
+  sc_keyvalue_entry_t *value;
+
+  /* Create the initial empty keyvalue object */
+  kv = sc_keyvalue_new ();
+
+  /* loop through the variable arguments to fill keyvalue */
+  for (;;) {
+    s = va_arg (ap, const char *);
+    if (s == NULL) {
+      break;
+    }
+    /* if this assertion blows then the type prefix might be missing */
+    SC_ASSERT (s[0] != '\0' && s[1] == ':' && s[2] != '\0');
+    value = (sc_keyvalue_entry_t *) sc_mempool_alloc (kv->value_allocator);
+    value->key = &s[2];
+    switch (s[0]) {
+    case 'i':
+      value->type = SC_KEYVALUE_ENTRY_INT;
+      value->value.i = va_arg (ap, int);
+      break;
+    case 'g':
+      value->type = SC_KEYVALUE_ENTRY_DOUBLE;
+      value->value.g = va_arg (ap, double);
+      break;
+    case 's':
+      value->type = SC_KEYVALUE_ENTRY_STRING;
+      value->value.s = va_arg (ap, const char *);
+      break;
+    case 'p':
+      value->type = SC_KEYVALUE_ENTRY_POINTER;
+      value->value.p = va_arg (ap, void *);
+      break;
+    default:
+      SC_ABORTF ("invalid argument character %c", s[0]);
+    }
+    added = sc_hash_insert_unique (kv->hash, value, &found);
+    if (!added) {
+      sc_mempool_free (kv->value_allocator, *found);
+      *found = value;
+    }
+  }
+
+  return kv;
+}
+
+sc_keyvalue_t      *
+sc_keyvalue_newf (int dummy, ...)
+{
+  va_list             ap;
+  sc_keyvalue_t      *kv;
+
+  SC_ASSERT (dummy == 0);
+
+  va_start (ap, dummy);
+  kv = sc_keyvalue_newv (ap);
+  va_end (ap);
+
+  return kv;
+}
+
+sc_keyvalue_t      *
+sc_keyvalue_new ()
+{
+  sc_keyvalue_t      *kv;
+
+  kv = SC_ALLOC (sc_keyvalue_t, 1);
+  kv->hash = sc_hash_new (sc_keyvalue_entry_hash, sc_keyvalue_entry_equal,
+                          NULL, NULL);
+  kv->value_allocator = sc_mempool_new (sizeof (sc_keyvalue_entry_t));
+
+  return kv;
+}
+
+void
+sc_keyvalue_destroy (sc_keyvalue_t * kv)
+{
+  sc_hash_destroy (kv->hash);
+  sc_mempool_destroy (kv->value_allocator);
+
+  SC_FREE (kv);
+}
+
+sc_keyvalue_entry_type_t
+sc_keyvalue_exists (sc_keyvalue_t * kv, const char *key)
+{
+  void              **found;
+  sc_keyvalue_entry_t svalue, *pvalue = &svalue;
+  sc_keyvalue_entry_t *value;
+
+  SC_ASSERT (kv != NULL);
+  SC_ASSERT (key != NULL);
+
+  pvalue->key = key;
+  pvalue->type = SC_KEYVALUE_ENTRY_NONE;
+  if (sc_hash_lookup (kv->hash, pvalue, &found)) {
+    value = (sc_keyvalue_entry_t *) (*found);
+    return value->type;
+  }
+  else
+    return SC_KEYVALUE_ENTRY_NONE;
+}
+
+sc_keyvalue_entry_type_t
+sc_keyvalue_unset (sc_keyvalue_t * kv, const char *key)
+{
+  void               *found;
+  sc_keyvalue_entry_t svalue, *pvalue = &svalue;
+  sc_keyvalue_entry_t *value;
+
+  int                 remove_test;
+  sc_keyvalue_entry_type_t type;
+
+  SC_ASSERT (kv != NULL);
+  SC_ASSERT (key != NULL);
+
+  pvalue->key = key;
+  pvalue->type = SC_KEYVALUE_ENTRY_NONE;
+
+  /* Remove this entry */
+  remove_test = sc_hash_remove (kv->hash, pvalue, &found);
+
+  /* Check whether anything was removed */
+  if (!remove_test)
+    return SC_KEYVALUE_ENTRY_NONE;
+
+  /* Code reaching this point must have found something */
+  SC_ASSERT (remove_test);
+  SC_ASSERT (found != NULL);
+
+  value = (sc_keyvalue_entry_t *) found;
+  type = value->type;
+
+  /* destroy the orignial hash entry */
+  sc_mempool_free (kv->value_allocator, value);
+
+  return type;
+}
+
+int
+sc_keyvalue_get_int (sc_keyvalue_t * kv, const char *key, int dvalue)
+{
+  void              **found;
+  sc_keyvalue_entry_t svalue, *pvalue = &svalue;
+  sc_keyvalue_entry_t *value;
+
+  SC_ASSERT (kv != NULL);
+  SC_ASSERT (key != NULL);
+
+  pvalue->key = key;
+  pvalue->type = SC_KEYVALUE_ENTRY_NONE;
+  if (sc_hash_lookup (kv->hash, pvalue, &found)) {
+    value = (sc_keyvalue_entry_t *) (*found);
+    SC_ASSERT (value->type == SC_KEYVALUE_ENTRY_INT);
+    return value->value.i;
+  }
+  else
+    return dvalue;
+}
+
+double
+sc_keyvalue_get_double (sc_keyvalue_t * kv, const char *key, double dvalue)
+{
+  void              **found;
+  sc_keyvalue_entry_t svalue, *pvalue = &svalue;
+  sc_keyvalue_entry_t *value;
+
+  SC_ASSERT (kv != NULL);
+  SC_ASSERT (key != NULL);
+
+  pvalue->key = key;
+  pvalue->type = SC_KEYVALUE_ENTRY_NONE;
+  if (sc_hash_lookup (kv->hash, pvalue, &found)) {
+    value = (sc_keyvalue_entry_t *) (*found);
+    SC_ASSERT (value->type == SC_KEYVALUE_ENTRY_DOUBLE);
+    return value->value.g;
+  }
+  else
+    return dvalue;
+}
+
+const char         *
+sc_keyvalue_get_string (sc_keyvalue_t * kv, const char *key,
+                        const char *dvalue)
+{
+  void              **found;
+  sc_keyvalue_entry_t svalue, *pvalue = &svalue;
+  sc_keyvalue_entry_t *value;
+
+  SC_ASSERT (kv != NULL);
+  SC_ASSERT (key != NULL);
+
+  pvalue->key = key;
+  pvalue->type = SC_KEYVALUE_ENTRY_NONE;
+  if (sc_hash_lookup (kv->hash, pvalue, &found)) {
+    value = (sc_keyvalue_entry_t *) (*found);
+    SC_ASSERT (value->type == SC_KEYVALUE_ENTRY_STRING);
+    return value->value.s;
+  }
+  else
+    return dvalue;
+}
+
+void               *
+sc_keyvalue_get_pointer (sc_keyvalue_t * kv, const char *key, void *dvalue)
+{
+  void              **found;
+  sc_keyvalue_entry_t svalue, *pvalue = &svalue;
+  sc_keyvalue_entry_t *value;
+
+  SC_ASSERT (kv != NULL);
+  SC_ASSERT (key != NULL);
+
+  pvalue->key = key;
+  pvalue->type = SC_KEYVALUE_ENTRY_NONE;
+  if (sc_hash_lookup (kv->hash, pvalue, &found)) {
+    value = (sc_keyvalue_entry_t *) (*found);
+    SC_ASSERT (value->type == SC_KEYVALUE_ENTRY_POINTER);
+    return value->value.p;
+  }
+  else
+    return dvalue;
+}
+
+void
+sc_keyvalue_set_int (sc_keyvalue_t * kv, const char *key, int newvalue)
+{
+  void              **found;
+  sc_keyvalue_entry_t svalue, *pvalue = &svalue;
+  sc_keyvalue_entry_t *value;
+
+  SC_ASSERT (kv != NULL);
+  SC_ASSERT (key != NULL);
+
+  pvalue->key = key;
+  pvalue->type = SC_KEYVALUE_ENTRY_NONE;
+  if (sc_hash_lookup (kv->hash, pvalue, &found)) {
+    /* Key already exists in hash table */
+    value = (sc_keyvalue_entry_t *) (*found);
+    SC_ASSERT (value->type == SC_KEYVALUE_ENTRY_INT);
+
+    value->value.i = newvalue;
+  }
+  else {
+    /* Key does not exist and must be created */
+    value = (sc_keyvalue_entry_t *) sc_mempool_alloc (kv->value_allocator);
+    value->key = key;
+    value->type = SC_KEYVALUE_ENTRY_INT;
+    value->value.i = newvalue;
+
+    /* Insert value into the hash table */
+    SC_EXECUTE_ASSERT_TRUE (sc_hash_insert_unique (kv->hash, value, &found));
+  }
+}
+
+void
+sc_keyvalue_set_double (sc_keyvalue_t * kv, const char *key, double newvalue)
+{
+  void              **found;
+  sc_keyvalue_entry_t svalue, *pvalue = &svalue;
+  sc_keyvalue_entry_t *value;
+
+  SC_ASSERT (kv != NULL);
+  SC_ASSERT (key != NULL);
+
+  pvalue->key = key;
+  pvalue->type = SC_KEYVALUE_ENTRY_NONE;
+  if (sc_hash_lookup (kv->hash, pvalue, &found)) {
+    /* Key already exists in hash table */
+    value = (sc_keyvalue_entry_t *) (*found);
+    SC_ASSERT (value->type == SC_KEYVALUE_ENTRY_DOUBLE);
+
+    value->value.g = newvalue;
+  }
+  else {
+    /* Key does not exist and must be created */
+    value = (sc_keyvalue_entry_t *) sc_mempool_alloc (kv->value_allocator);
+    value->key = key;
+    value->type = SC_KEYVALUE_ENTRY_DOUBLE;
+    value->value.g = newvalue;
+
+    /* Insert value into the hash table */
+    SC_EXECUTE_ASSERT_TRUE (sc_hash_insert_unique (kv->hash, value, &found));
+  }
+}
+
+void
+sc_keyvalue_set_string (sc_keyvalue_t * kv, const char *key,
+                        const char *newvalue)
+{
+  void              **found;
+  sc_keyvalue_entry_t svalue, *pvalue = &svalue;
+  sc_keyvalue_entry_t *value;
+
+  SC_ASSERT (kv != NULL);
+  SC_ASSERT (key != NULL);
+
+  pvalue->key = key;
+  pvalue->type = SC_KEYVALUE_ENTRY_NONE;
+  if (sc_hash_lookup (kv->hash, pvalue, &found)) {
+    /* Key already exists in hash table */
+    value = (sc_keyvalue_entry_t *) (*found);
+    SC_ASSERT (value->type == SC_KEYVALUE_ENTRY_STRING);
+
+    value->value.s = newvalue;
+  }
+  else {
+    /* Key does not exist and must be created */
+    value = (sc_keyvalue_entry_t *) sc_mempool_alloc (kv->value_allocator);
+    value->key = key;
+    value->type = SC_KEYVALUE_ENTRY_STRING;
+    value->value.s = newvalue;
+
+    /* Insert value into the hash table */
+    SC_EXECUTE_ASSERT_TRUE (sc_hash_insert_unique (kv->hash, value, &found));
+  }
+}
+
+void
+sc_keyvalue_set_pointer (sc_keyvalue_t * kv, const char *key, void *newvalue)
+{
+  void              **found;
+  sc_keyvalue_entry_t svalue, *pvalue = &svalue;
+  sc_keyvalue_entry_t *value;
+
+  SC_ASSERT (kv != NULL);
+  SC_ASSERT (key != NULL);
+
+  pvalue->key = key;
+  pvalue->type = SC_KEYVALUE_ENTRY_NONE;
+  if (sc_hash_lookup (kv->hash, pvalue, &found)) {
+    /* Key already exists in hash table */
+    value = (sc_keyvalue_entry_t *) (*found);
+    SC_ASSERT (value->type == SC_KEYVALUE_ENTRY_POINTER);
+
+    value->value.p = (void *) newvalue;
+  }
+  else {
+    /* Key does not exist and must be created */
+    value = (sc_keyvalue_entry_t *) sc_mempool_alloc (kv->value_allocator);
+    value->key = key;
+    value->type = SC_KEYVALUE_ENTRY_POINTER;
+    value->value.p = (void *) newvalue;
+
+    /* Insert value into the hash table */
+    SC_EXECUTE_ASSERT_TRUE (sc_hash_insert_unique (kv->hash, value, &found));
+  }
+}
+
+typedef struct sc_kv_hash_data
+{
+  sc_keyvalue_foreach_t fn;
+  void               *data;
+}
+sc_kv_hash_data_t;
+
+static int
+sc_kv_hash_fn (void **v, const void *u)
+{
+  const sc_kv_hash_data_t *hdata = (const sc_kv_hash_data_t *) u;
+  sc_keyvalue_entry_t *hentry = (sc_keyvalue_entry_t *) * v;
+  const char         *key = hentry->key;
+  sc_keyvalue_entry_type_t type = hentry->type;
+  void               *entry = &(hentry->value.p);
+
+  return hdata->fn (key, type, entry, hdata->data);
+}
+
+void
+sc_keyvalue_foreach (sc_keyvalue_t * kv, sc_keyvalue_foreach_t fn,
+                     void *user_data)
+{
+  sc_kv_hash_data_t   hdata;
+
+  hdata.fn = fn;
+  hdata.data = user_data;
+
+  SC_ASSERT (kv->hash->user_data == NULL);
+  kv->hash->user_data = &hdata;
+
+  sc_hash_foreach (kv->hash, sc_kv_hash_fn);
+
+  kv->hash->user_data = NULL;
+}
diff --git a/sc/src/sc_keyvalue.h b/sc/src/sc_keyvalue.h
new file mode 100644
index 0000000..82184bc
--- /dev/null
+++ b/sc/src/sc_keyvalue.h
@@ -0,0 +1,108 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_KEYVALUE_H
+#define SC_KEYVALUE_H
+
+#include <sc.h>
+#include <sc_containers.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef enum
+{
+  SC_KEYVALUE_ENTRY_NONE = 0,
+  SC_KEYVALUE_ENTRY_INT,
+  SC_KEYVALUE_ENTRY_DOUBLE,
+  SC_KEYVALUE_ENTRY_STRING,
+  SC_KEYVALUE_ENTRY_POINTER
+}
+sc_keyvalue_entry_type_t;
+
+typedef struct sc_keyvalue
+{
+  sc_hash_t          *hash;
+  sc_mempool_t       *value_allocator;
+}
+sc_keyvalue_t;
+
+/* Constructors / destructors */
+sc_keyvalue_t      *sc_keyvalue_new ();
+
+/* Arguments come in pairs of 2: static string "type:key" and value;
+   type is a letter like the identifier names in sc_keyvalue_entry.value */
+sc_keyvalue_t      *sc_keyvalue_newf (int dummy, ...);
+sc_keyvalue_t      *sc_keyvalue_newv (va_list ap);
+
+void                sc_keyvalue_destroy (sc_keyvalue_t * kv);
+
+/* Routine to check existence of an entry
+   returns the type if found, and SC_KEYVALUE_ENTRY_NONE otherwise */
+sc_keyvalue_entry_type_t sc_keyvalue_exists (sc_keyvalue_t * kv,
+                                             const char *key);
+
+/* Routine to remove an entry
+   returne type if found and removed, SC_KEYVALUE_ENTRY_NONE otherwise */
+sc_keyvalue_entry_type_t sc_keyvalue_unset (sc_keyvalue_t * kv,
+                                            const char *key);
+
+/* Routines to extract values from keys
+   if the key is not present then dvalue is returned */
+int                 sc_keyvalue_get_int (sc_keyvalue_t * kv,
+                                         const char *key, int dvalue);
+double              sc_keyvalue_get_double (sc_keyvalue_t * kv,
+                                            const char *key, double dvalue);
+const char         *sc_keyvalue_get_string (sc_keyvalue_t * kv,
+                                            const char *key,
+                                            const char *dvalue);
+void               *sc_keyvalue_get_pointer (sc_keyvalue_t * kv,
+                                             const char *key, void *dvalue);
+
+/* Routines to set values for a given key */
+void                sc_keyvalue_set_int (sc_keyvalue_t * kv,
+                                         const char *key, int newvalue);
+void                sc_keyvalue_set_double (sc_keyvalue_t * kv,
+                                            const char *key, double newvalue);
+void                sc_keyvalue_set_string (sc_keyvalue_t * kv,
+                                            const char *key,
+                                            const char *newvalue);
+void                sc_keyvalue_set_pointer (sc_keyvalue_t * kv,
+                                             const char *key, void *newvalue);
+
+/** Function to call on every key value pair
+ * \param [in] key   The key for this pair
+ * \param [in] type  The type of entry
+ * \param [in] entry Pointer to the entry
+ * \param [in] u     Arbitrary user data.
+ * \return Return true if the traversal should continue, false to stop.
+ */
+typedef int         (*sc_keyvalue_foreach_t) (const char *key,
+                                              const sc_keyvalue_entry_type_t
+                                              type, void *entry,
+                                              const void *u);
+
+void                sc_keyvalue_foreach (sc_keyvalue_t * kv,
+                                         sc_keyvalue_foreach_t fn,
+                                         void *user_data);
+SC_EXTERN_C_END;
+
+#endif /* !SC_KEYVALUE_H */
diff --git a/sc/src/sc_lapack.c b/sc/src/sc_lapack.c
new file mode 100644
index 0000000..5375d19
--- /dev/null
+++ b/sc/src/sc_lapack.c
@@ -0,0 +1,36 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_lapack.h>
+
+const char          sc_jobzchar[] = { 'N', 'V', '?' };
+
+#ifndef SC_WITH_LAPACK
+
+int
+sc_lapack_nonimplemented ()
+{
+  SC_ABORT ("LAPACK not compiled in this configuration");
+  return 0;
+}
+
+#endif /* !SC_WITH_LAPACK */
diff --git a/sc/src/sc_lapack.h b/sc/src/sc_lapack.h
new file mode 100644
index 0000000..cfc9850
--- /dev/null
+++ b/sc/src/sc_lapack.h
@@ -0,0 +1,126 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_LAPACK_H
+#define SC_LAPACK_H
+
+#include <sc_blas.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef enum sc_jobz
+{
+  SC_EIGVALS_ONLY,
+  SC_EIGVALS_AND_EIGVECS,
+  SC_JOBZ_ANCHOR
+}
+sc_jobz_t;
+
+extern const char   sc_jobzchar[];
+
+#ifdef SC_WITH_LAPACK
+
+#ifndef SC_F77_FUNC
+#define SC_F77_FUNC(small,CAPS) small ## _
+#endif
+
+#define SC_LAPACK_DGELS   SC_F77_FUNC(dgels,DGELS)
+#define SC_LAPACK_DGETRF  SC_F77_FUNC(dgetrf,DGETRF)
+#define SC_LAPACK_DGETRS  SC_F77_FUNC(dgetrs,DGETRS)
+#define SC_LAPACK_DSTEV   SC_F77_FUNC(dstev,DSTEV)
+#define SC_LAPACK_DTRSM   SC_F77_FUNC(dtrsm,DTRSM)
+#define SC_LAPACK_DLAIC1  SC_F77_FUNC(dlaic1,DLAIC1)
+#define SC_LAPACK_ILAENV  SC_F77_FUNC(ilaenv,ILAENV)
+
+void                SC_LAPACK_DGELS (const char *trans,
+                                     const sc_bint_t * m, const sc_bint_t * n,
+                                     const sc_bint_t * nrhs, double *a,
+                                     const sc_bint_t * lda, double *b,
+                                     const sc_bint_t * ldb, double *work,
+                                     const sc_bint_t * lwork,
+                                     sc_bint_t * info);
+void                SC_LAPACK_DGETRF (const sc_bint_t * m,
+                                      const sc_bint_t * n, double *a,
+                                      const sc_bint_t * lda, sc_bint_t * ipiv,
+                                      sc_bint_t * info);
+
+void                SC_LAPACK_DGETRS (const char *trans, const sc_bint_t * n,
+                                      const sc_bint_t * nrhs, const double *a,
+                                      const sc_bint_t * lda,
+                                      const sc_bint_t * ipiv, double *b,
+                                      const sc_bint_t * ldx,
+                                      sc_bint_t * info);
+
+void                SC_LAPACK_DSTEV (const char *jobz,
+                                     const sc_bint_t * n,
+                                     double *d,
+                                     double *e,
+                                     double *z,
+                                     const sc_bint_t * ldz,
+                                     double *work, sc_bint_t * info);
+
+void                SC_LAPACK_DTRSM (const char *side,
+                                     const char *uplo,
+                                     const char *transa,
+                                     const char *diag,
+                                     const sc_bint_t * m,
+                                     const sc_bint_t * n,
+                                     const double *alpha,
+                                     const double *a,
+                                     const sc_bint_t * lda,
+                                     const double *b, const sc_bint_t * ldb);
+
+void                SC_LAPACK_DLAIC1 (const int *job,
+                                      const int *j,
+                                      const double *x,
+                                      const double *sest,
+                                      const double *w,
+                                      const double *gamma,
+                                      double *sestpr, double *s, double *c);
+
+int                 SC_LAPACK_ILAENV (const sc_bint_t * ispec,
+                                      const char *name,
+                                      const char *opts,
+                                      const sc_bint_t * N1,
+                                      const sc_bint_t * N2,
+                                      const sc_bint_t * N3,
+                                      const sc_bint_t * N4,
+                                      sc_buint_t name_length,
+                                      sc_buint_t opts_length);
+
+#else /* !SC_WITH_LAPACK */
+
+#define SC_LAPACK_DGELS    (void) sc_lapack_nonimplemented
+#define SC_LAPACK_DGETRF   (void) sc_lapack_nonimplemented
+#define SC_LAPACK_DGETRS   (void) sc_lapack_nonimplemented
+#define SC_LAPACK_DSTEV    (void) sc_lapack_nonimplemented
+#define SC_LAPACK_DTRSM    (void) sc_lapack_nonimplemented
+#define SC_LAPACK_DLAIC1   (void) sc_lapack_nonimplemented
+#define SC_LAPACK_ILAENV   (int)  sc_lapack_nonimplemented
+
+int                 sc_lapack_nonimplemented ();
+
+#endif
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_LAPACK_H */
diff --git a/sc/src/sc_lua.h b/sc/src/sc_lua.h
new file mode 100644
index 0000000..e1813aa
--- /dev/null
+++ b/sc/src/sc_lua.h
@@ -0,0 +1,56 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_LUA_H
+#define SC_LUA_H
+
+#include <sc.h>
+
+#ifdef SC_HAVE_LUA
+
+SC_EXTERN_C_BEGIN;
+
+#ifdef SC_HAVE_LUA5_2_LUA_H
+#include <lua5.2/lua.h>
+#include <lua5.2/lualib.h>
+#include <lua5.2/lauxlib.h>
+#else
+#ifdef SC_HAVE_LUA5_1_LUA_H
+#include <lua5.1/lua.h>
+#include <lua5.1/lualib.h>
+#include <lua5.1/lauxlib.h>
+#else
+#ifdef SC_HAVE_LUA_H
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#endif
+#endif
+#endif
+
+SC_EXTERN_C_END;
+
+#else
+#error "We did not find a recent lua library that provides lua_createtable."
+#endif
+
+#endif /* !SC_LUA_H */
diff --git a/sc/src/sc_mpi.c b/sc/src/sc_mpi.c
new file mode 100644
index 0000000..dc1d31d
--- /dev/null
+++ b/sc/src/sc_mpi.c
@@ -0,0 +1,358 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+/* including sc_mpi.h does not work here since sc_mpi.h is included by sc.h */
+#include <sc.h>
+
+#ifndef SC_ENABLE_MPI
+
+/* gettimeofday is in either of these two */
+#ifdef SC_HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef SC_HAVE_TIME_H
+#include <time.h>
+#endif
+
+static inline void
+mpi_dummy_assert_op (sc_MPI_Op op)
+{
+  switch (op) {
+  case sc_MPI_MAX:
+  case sc_MPI_MIN:
+  case sc_MPI_SUM:
+  case sc_MPI_PROD:
+  case sc_MPI_LAND:
+  case sc_MPI_BAND:
+  case sc_MPI_LOR:
+  case sc_MPI_BOR:
+  case sc_MPI_LXOR:
+  case sc_MPI_BXOR:
+  case sc_MPI_MINLOC:
+  case sc_MPI_MAXLOC:
+  case sc_MPI_REPLACE:
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+}
+
+int
+sc_MPI_Init (int *argc, char ***argv)
+{
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Init_thread (int *argc, char ***argv, int required, int *provided)
+{
+  if (provided != NULL) {
+    *provided = sc_MPI_THREAD_SINGLE;
+  }
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Finalize (void)
+{
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Abort (sc_MPI_Comm comm, int exitcode)
+{
+  abort ();
+}
+
+int
+sc_MPI_Comm_dup (sc_MPI_Comm comm, sc_MPI_Comm * newcomm)
+{
+  *newcomm = comm;
+
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Comm_free (sc_MPI_Comm * comm)
+{
+  *comm = sc_MPI_COMM_NULL;
+
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Comm_size (sc_MPI_Comm comm, int *size)
+{
+  *size = 1;
+
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Comm_rank (sc_MPI_Comm comm, int *rank)
+{
+  *rank = 0;
+
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Barrier (sc_MPI_Comm comm)
+{
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Bcast (void *p, int n, sc_MPI_Datatype t, int rank, sc_MPI_Comm comm)
+{
+  SC_ASSERT (rank == 0);
+
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Gather (void *p, int np, sc_MPI_Datatype tp,
+               void *q, int nq, sc_MPI_Datatype tq, int rank,
+               sc_MPI_Comm comm)
+{
+  size_t              lp, lq;
+
+  SC_ASSERT (rank == 0 && np >= 0 && nq >= 0);
+
+/* *INDENT-OFF* horrible indent bug */
+  lp = (size_t) np * sc_mpi_sizeof (tp);
+  lq = (size_t) nq * sc_mpi_sizeof (tq);
+/* *INDENT-ON* */
+
+  SC_ASSERT (lp == lq);
+  memcpy (q, p, lp);
+
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Gatherv (void *p, int np, sc_MPI_Datatype tp,
+                void *q, int *recvc, int *displ,
+                sc_MPI_Datatype tq, int rank, sc_MPI_Comm comm)
+{
+  int                 nq;
+  size_t              lp, lq;
+
+  nq = recvc[0];
+  SC_ASSERT (rank == 0 && np >= 0 && nq >= 0);
+
+/* *INDENT-OFF* horrible indent bug */
+  lp = (size_t) np * sc_mpi_sizeof (tp);
+  lq = (size_t) nq * sc_mpi_sizeof (tq);
+/* *INDENT-ON* */
+
+  SC_ASSERT (lp == lq);
+  memcpy ((char *) q + displ[0] * sc_mpi_sizeof (tq), p, lp);
+
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Allgather (void *p, int np, sc_MPI_Datatype tp,
+                  void *q, int nq, sc_MPI_Datatype tq, sc_MPI_Comm comm)
+{
+  return sc_MPI_Gather (p, np, tp, q, nq, tq, 0, comm);
+}
+
+int
+sc_MPI_Allgatherv (void *p, int np, sc_MPI_Datatype tp,
+                   void *q, int *recvc, int *displ,
+                   sc_MPI_Datatype tq, sc_MPI_Comm comm)
+{
+  return sc_MPI_Gatherv (p, np, tp, q, recvc, displ, tq, 0, comm);
+}
+
+int
+sc_MPI_Reduce (void *p, void *q, int n, sc_MPI_Datatype t,
+               sc_MPI_Op op, int rank, sc_MPI_Comm comm)
+{
+  size_t              l;
+
+  SC_ASSERT (rank == 0 && n >= 0);
+  mpi_dummy_assert_op (op);
+
+/* *INDENT-OFF* horrible indent bug */
+  l = (size_t) n * sc_mpi_sizeof (t);
+/* *INDENT-ON* */
+
+  memcpy (q, p, l);
+
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Allreduce (void *p, void *q, int n, sc_MPI_Datatype t,
+                  sc_MPI_Op op, sc_MPI_Comm comm)
+{
+  return sc_MPI_Reduce (p, q, n, t, op, 0, comm);
+}
+
+int
+sc_MPI_Recv (void *buf, int count, sc_MPI_Datatype datatype, int source,
+             int tag, sc_MPI_Comm comm, sc_MPI_Status * status)
+{
+  SC_ABORT ("non-MPI MPI_Recv is not implemented");
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Irecv (void *buf, int count, sc_MPI_Datatype datatype, int source,
+              int tag, sc_MPI_Comm comm, sc_MPI_Request * request)
+{
+  SC_ABORT ("non-MPI MPI_Irecv is not implemented");
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Send (void *buf, int count, sc_MPI_Datatype datatype,
+             int dest, int tag, sc_MPI_Comm comm)
+{
+  SC_ABORT ("non-MPI MPI_Send is not implemented");
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Isend (void *buf, int count, sc_MPI_Datatype datatype, int dest,
+              int tag, sc_MPI_Comm comm, sc_MPI_Request * request)
+{
+  SC_ABORT ("non-MPI MPI_Isend is not implemented");
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Probe (int source, int tag, sc_MPI_Comm comm, sc_MPI_Status * status)
+{
+  SC_ABORT ("non-MPI MPI_Probe is not implemented");
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Iprobe (int source, int tag, sc_MPI_Comm comm, int *flag,
+               sc_MPI_Status * status)
+{
+  SC_ABORT ("non-MPI MPI_Iprobe is not implemented");
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Get_count (sc_MPI_Status * status, sc_MPI_Datatype datatype,
+                  int *count)
+{
+  SC_ABORT ("non-MPI MPI_Get_count is not implemented");
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Wait (sc_MPI_Request * request, sc_MPI_Status * status)
+{
+  SC_CHECK_ABORT (*request == sc_MPI_REQUEST_NULL,
+                  "non-MPI MPI_Wait handles NULL request only");
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Waitsome (int incount, sc_MPI_Request * array_of_requests,
+                 int *outcount, int *array_of_indices,
+                 sc_MPI_Status * array_of_statuses)
+{
+  int                 i;
+
+  for (i = 0; i < incount; ++i) {
+    SC_CHECK_ABORT (array_of_requests[i] == sc_MPI_REQUEST_NULL,
+                    "non-MPI MPI_Waitsome handles NULL requests only");
+  }
+  *outcount = 0;
+
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_MPI_Waitall (int count, sc_MPI_Request * array_of_requests,
+                sc_MPI_Status * array_of_statuses)
+{
+  int                 i;
+
+  for (i = 0; i < count; ++i) {
+    SC_CHECK_ABORT (array_of_requests[i] == sc_MPI_REQUEST_NULL,
+                    "non-MPI MPI_Waitall handles NULL requests only");
+  }
+  return sc_MPI_SUCCESS;
+}
+
+double
+sc_MPI_Wtime (void)
+{
+  int                 retval;
+  struct timeval      tv;
+
+  retval = gettimeofday (&tv, NULL);
+  SC_CHECK_ABORT (retval == 0, "gettimeofday");
+
+  return (double) tv.tv_sec + 1.e-6 * tv.tv_usec;
+}
+
+#else /* SC_ENABLE_MPI */
+#ifndef SC_ENABLE_MPITHREAD
+
+/* default to non-threaded operation */
+
+int
+sc_MPI_Init_thread (int *argc, char ***argv, int required, int *provided)
+{
+  if (provided != NULL) {
+    *provided = sc_MPI_THREAD_SINGLE;
+  }
+  return sc_MPI_Init (argc, argv);
+}
+
+#endif /* !SC_ENABLE_MPITHREAD */
+#endif /* SC_ENABLE_MPI */
+
+size_t
+sc_mpi_sizeof (sc_MPI_Datatype t)
+{
+  if (t == sc_MPI_CHAR)
+    return sizeof (char);
+  if (t == sc_MPI_BYTE)
+    return 1;
+  if (t == sc_MPI_SHORT || t == sc_MPI_UNSIGNED_SHORT)
+    return sizeof (short);
+  if (t == sc_MPI_INT || t == sc_MPI_UNSIGNED)
+    return sizeof (int);
+  if (t == sc_MPI_LONG || t == sc_MPI_UNSIGNED_LONG)
+    return sizeof (long);
+  if (t == sc_MPI_LONG_LONG_INT)
+    return sizeof (long long);
+  if (t == sc_MPI_FLOAT)
+    return sizeof (float);
+  if (t == sc_MPI_DOUBLE)
+    return sizeof (double);
+  if (t == sc_MPI_LONG_DOUBLE)
+    return sizeof (long double);
+
+  SC_ABORT_NOT_REACHED ();
+}
diff --git a/sc/src/sc_mpi.h b/sc/src/sc_mpi.h
new file mode 100644
index 0000000..601239d
--- /dev/null
+++ b/sc/src/sc_mpi.h
@@ -0,0 +1,301 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+/** \file
+ *
+ * This file emulates collective MPI routines for non-MPI code.
+ *
+ * The goal is to make code compile and execute cleanly when `--enable-mpi` is
+ * not given on the configure line.  To this end, several MPI routines that are
+ * meaningful to call on one processor are provided with the prefix `sc_MPI_`,
+ * as well as necessary types and defines.  If `--enable-mpi` is given, this
+ * file provides macros that map the sc_-prefixed form to the standard form of
+ * the symbols.
+ *
+ * When including this file in your code, everything inside `#ifdef
+ * SC_ENABLE_MPI` can use the standard MPI API.  Outside of this define, you
+ * may use the sc_MPI_* routines specified here to seamlessly use MPI calls.
+ *
+ * Some send and receive routines are wrapped.  They can thus be used
+ * in code outside of `#ifdef SC_ENABLE_MPI` even though they will abort.  If
+ * no messages are sent to the same processor when mpisize == 1, such aborts
+ * will not occur.  The `MPI_Wait*` routines are safe to call as long as no or
+ * only MPI_REQUEST_NULL requests are passed in.
+ */
+
+#ifndef SC_MPI_H
+#define SC_MPI_H
+
+#include <sc.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef enum
+{
+  SC_TAG_AG_ALLTOALL = 's' + 'c',       /* anything really */
+  SC_TAG_AG_RECURSIVE_A,
+  SC_TAG_AG_RECURSIVE_B,
+  SC_TAG_AG_RECURSIVE_C,
+  SC_TAG_NOTIFY_RECURSIVE,
+  SC_TAG_REDUCE,
+  SC_TAG_PSORT_LO,
+  SC_TAG_PSORT_HI
+}
+sc_tag_t;
+
+#ifdef SC_ENABLE_MPI
+
+/* constants */
+
+#define sc_MPI_SUCCESS             MPI_SUCCESS
+#define sc_MPI_COMM_NULL           MPI_COMM_NULL
+#define sc_MPI_COMM_WORLD          MPI_COMM_WORLD
+#define sc_MPI_COMM_SELF           MPI_COMM_SELF
+
+#define sc_MPI_ANY_SOURCE          MPI_ANY_SOURCE
+#define sc_MPI_ANY_TAG             MPI_ANY_TAG
+#define sc_MPI_STATUS_IGNORE       MPI_STATUS_IGNORE
+#define sc_MPI_STATUSES_IGNORE     MPI_STATUSES_IGNORE
+
+#define sc_MPI_REQUEST_NULL        MPI_REQUEST_NULL
+
+#define sc_MPI_CHAR                MPI_CHAR
+#define sc_MPI_SIGNED_CHAR         MPI_SIGNED_CHAR
+#define sc_MPI_UNSIGNED_CHAR       MPI_UNSIGNED_CHAR
+#define sc_MPI_BYTE                MPI_BYTE
+#define sc_MPI_SHORT               MPI_SHORT
+#define sc_MPI_UNSIGNED_SHORT      MPI_UNSIGNED_SHORT
+#define sc_MPI_INT                 MPI_INT
+#define sc_MPI_UNSIGNED            MPI_UNSIGNED
+#define sc_MPI_LONG                MPI_LONG
+#define sc_MPI_UNSIGNED_LONG       MPI_UNSIGNED_LONG
+#define sc_MPI_LONG_LONG_INT       MPI_LONG_LONG_INT
+#define sc_MPI_FLOAT               MPI_FLOAT
+#define sc_MPI_DOUBLE              MPI_DOUBLE
+#define sc_MPI_LONG_DOUBLE         MPI_LONG_DOUBLE
+
+#define sc_MPI_MAX                 MPI_MAX
+#define sc_MPI_MIN                 MPI_MIN
+#define sc_MPI_SUM                 MPI_SUM
+#define sc_MPI_PROD                MPI_PROD
+#define sc_MPI_LAND                MPI_LAND
+#define sc_MPI_BAND                MPI_BAND
+#define sc_MPI_LOR                 MPI_LOR
+#define sc_MPI_BOR                 MPI_BOR
+#define sc_MPI_LXOR                MPI_LXOR
+#define sc_MPI_BXOR                MPI_BXOR
+#define sc_MPI_MINLOC              MPI_MINLOC
+#define sc_MPI_MAXLOC              MPI_MAXLOC
+#define sc_MPI_REPLACE             MPI_REPLACE
+
+#define sc_MPI_UNDEFINED           MPI_UNDEFINED
+
+/* types */
+
+#define sc_MPI_Comm                MPI_Comm
+#define sc_MPI_Datatype            MPI_Datatype
+#define sc_MPI_Op                  MPI_Op
+#define sc_MPI_Request             MPI_Request
+#define sc_MPI_Status              MPI_Status
+
+/* functions */
+
+#define sc_MPI_Init                MPI_Init
+/*      sc_MPI_Init_thread is handled below */
+#define sc_MPI_Finalize            MPI_Finalize
+#define sc_MPI_Abort               MPI_Abort
+#define sc_MPI_Comm_dup            MPI_Comm_dup
+#define sc_MPI_Comm_free           MPI_Comm_free
+#define sc_MPI_Comm_size           MPI_Comm_size
+#define sc_MPI_Comm_rank           MPI_Comm_rank
+#define sc_MPI_Barrier             MPI_Barrier
+#define sc_MPI_Bcast               MPI_Bcast
+#define sc_MPI_Gather              MPI_Gather
+#define sc_MPI_Gatherv             MPI_Gatherv
+#define sc_MPI_Allgather           MPI_Allgather
+#define sc_MPI_Allgatherv          MPI_Allgatherv
+#define sc_MPI_Reduce              MPI_Reduce
+#define sc_MPI_Allreduce           MPI_Allreduce
+#define sc_MPI_Recv                MPI_Recv
+#define sc_MPI_Irecv               MPI_Irecv
+#define sc_MPI_Send                MPI_Send
+#define sc_MPI_Isend               MPI_Isend
+#define sc_MPI_Probe               MPI_Probe
+#define sc_MPI_Iprobe              MPI_Iprobe
+#define sc_MPI_Get_count           MPI_Get_count
+#define sc_MPI_Wtime               MPI_Wtime
+#define sc_MPI_Wait                MPI_Wait
+#define sc_MPI_Waitsome            MPI_Waitsome
+#define sc_MPI_Waitall             MPI_Waitall
+
+#else /* !SC_ENABLE_MPI */
+
+/* constants */
+
+#define sc_MPI_SUCCESS             0
+#define sc_MPI_COMM_NULL           ((sc_MPI_Comm) 0x04000000)
+#define sc_MPI_COMM_WORLD          ((sc_MPI_Comm) 0x44000000)
+#define sc_MPI_COMM_SELF           ((sc_MPI_Comm) 0x44000001)
+
+#define sc_MPI_ANY_SOURCE          (-2)
+#define sc_MPI_ANY_TAG             (-1)
+#define sc_MPI_STATUS_IGNORE       (sc_MPI_Status *) 1
+#define sc_MPI_STATUSES_IGNORE     (sc_MPI_Status *) 1
+
+#define sc_MPI_REQUEST_NULL        ((sc_MPI_Request) 0x2c000000)
+
+#define sc_MPI_CHAR                ((sc_MPI_Datatype) 0x4c000101)
+#define sc_MPI_SIGNED_CHAR         ((sc_MPI_Datatype) 0x4c000118)
+#define sc_MPI_UNSIGNED_CHAR       ((sc_MPI_Datatype) 0x4c000102)
+#define sc_MPI_BYTE                ((sc_MPI_Datatype) 0x4c00010d)
+#define sc_MPI_SHORT               ((sc_MPI_Datatype) 0x4c000203)
+#define sc_MPI_UNSIGNED_SHORT      ((sc_MPI_Datatype) 0x4c000204)
+#define sc_MPI_INT                 ((sc_MPI_Datatype) 0x4c000405)
+#define sc_MPI_UNSIGNED            ((sc_MPI_Datatype) 0x4c000406)
+#define sc_MPI_LONG                ((sc_MPI_Datatype) 0x4c000407)
+#define sc_MPI_UNSIGNED_LONG       ((sc_MPI_Datatype) 0x4c000408)
+#define sc_MPI_LONG_LONG_INT       ((sc_MPI_Datatype) 0x4c000809)
+#define sc_MPI_FLOAT               ((sc_MPI_Datatype) 0x4c00040a)
+#define sc_MPI_DOUBLE              ((sc_MPI_Datatype) 0x4c00080b)
+#define sc_MPI_LONG_DOUBLE         ((sc_MPI_Datatype) 0x4c000c0c)
+
+#define sc_MPI_MAX                 ((sc_MPI_Op) 0x58000001)
+#define sc_MPI_MIN                 ((sc_MPI_Op) 0x58000002)
+#define sc_MPI_SUM                 ((sc_MPI_Op) 0x58000003)
+#define sc_MPI_PROD                ((sc_MPI_Op) 0x58000004)
+#define sc_MPI_LAND                ((sc_MPI_Op) 0x58000005)
+#define sc_MPI_BAND                ((sc_MPI_Op) 0x58000006)
+#define sc_MPI_LOR                 ((sc_MPI_Op) 0x58000007)
+#define sc_MPI_BOR                 ((sc_MPI_Op) 0x58000008)
+#define sc_MPI_LXOR                ((sc_MPI_Op) 0x58000009)
+#define sc_MPI_BXOR                ((sc_MPI_Op) 0x5800000a)
+#define sc_MPI_MINLOC              ((sc_MPI_Op) 0x5800000b)
+#define sc_MPI_MAXLOC              ((sc_MPI_Op) 0x5800000c)
+#define sc_MPI_REPLACE             ((sc_MPI_Op) 0x5800000d)
+
+#define sc_MPI_UNDEFINED           (-32766)
+
+/* types */
+
+typedef int         sc_MPI_Comm;
+typedef int         sc_MPI_Datatype;
+typedef int         sc_MPI_Op;
+typedef int         sc_MPI_Request;
+typedef struct sc_MPI_Status
+{
+  int                 count;
+  int                 cancelled;
+  int                 MPI_SOURCE;
+  int                 MPI_TAG;
+  int                 MPI_ERROR;
+}
+sc_MPI_Status;
+
+/* These functions are valid and functional for a single process. */
+
+int                 sc_MPI_Init (int *, char ***);
+/*                  sc_MPI_Init_thread is handled below */
+
+int                 sc_MPI_Finalize (void);
+int                 sc_MPI_Abort (sc_MPI_Comm, int)
+  __attribute__ ((noreturn));
+
+int                 sc_MPI_Comm_dup (sc_MPI_Comm, sc_MPI_Comm *);
+int                 sc_MPI_Comm_free (sc_MPI_Comm *);
+int                 sc_MPI_Comm_size (sc_MPI_Comm, int *);
+int                 sc_MPI_Comm_rank (sc_MPI_Comm, int *);
+
+int                 sc_MPI_Barrier (sc_MPI_Comm);
+int                 sc_MPI_Bcast (void *, int, sc_MPI_Datatype, int,
+                                  sc_MPI_Comm);
+int                 sc_MPI_Gather (void *, int, sc_MPI_Datatype, void *, int,
+                                   sc_MPI_Datatype, int, sc_MPI_Comm);
+int                 sc_MPI_Gatherv (void *, int, sc_MPI_Datatype, void *,
+                                    int *, int *, sc_MPI_Datatype, int,
+                                    sc_MPI_Comm);
+int                 sc_MPI_Allgather (void *, int, sc_MPI_Datatype, void *,
+                                      int, sc_MPI_Datatype, sc_MPI_Comm);
+int                 sc_MPI_Allgatherv (void *, int, sc_MPI_Datatype, void *,
+                                       int *, int *, sc_MPI_Datatype,
+                                       sc_MPI_Comm);
+int                 sc_MPI_Reduce (void *, void *, int, sc_MPI_Datatype,
+                                   sc_MPI_Op, int, sc_MPI_Comm);
+int                 sc_MPI_Allreduce (void *, void *, int, sc_MPI_Datatype,
+                                      sc_MPI_Op, sc_MPI_Comm);
+
+double              sc_MPI_Wtime (void);
+
+/* These functions will abort. */
+
+int                 sc_MPI_Recv (void *, int, sc_MPI_Datatype, int, int,
+                                 sc_MPI_Comm, sc_MPI_Status *);
+int                 sc_MPI_Irecv (void *, int, sc_MPI_Datatype, int, int,
+                                  sc_MPI_Comm, sc_MPI_Request *);
+int                 sc_MPI_Send (void *, int, sc_MPI_Datatype, int, int,
+                                 sc_MPI_Comm);
+int                 sc_MPI_Isend (void *, int, sc_MPI_Datatype, int, int,
+                                  sc_MPI_Comm, sc_MPI_Request *);
+int                 sc_MPI_Probe (int, int, sc_MPI_Comm, sc_MPI_Status *);
+int                 sc_MPI_Iprobe (int, int, sc_MPI_Comm, int *,
+                                   sc_MPI_Status *);
+int                 sc_MPI_Get_count (sc_MPI_Status *, sc_MPI_Datatype,
+                                      int *);
+
+/* These functions are only allowed to be called with NULL requests. */
+
+int                 sc_MPI_Wait (sc_MPI_Request *, sc_MPI_Status *);
+int                 sc_MPI_Waitsome (int, sc_MPI_Request *,
+                                     int *, int *, sc_MPI_Status *);
+int                 sc_MPI_Waitall (int, sc_MPI_Request *, sc_MPI_Status *);
+
+#endif /* !SC_ENABLE_MPI */
+
+#if defined SC_ENABLE_MPI && defined SC_ENABLE_MPITHREAD
+
+#define sc_MPI_THREAD_SINGLE       MPI_THREAD_SINGLE
+#define sc_MPI_THREAD_FUNNELED     MPI_THREAD_FUNNELED
+#define sc_MPI_THREAD_SERIALIZED   MPI_THREAD_SERIALIZED
+#define sc_MPI_THREAD_MULTIPLE     MPI_THREAD_MULTIPLE
+
+#define sc_MPI_Init_thread         MPI_Init_thread
+
+#else
+
+#define sc_MPI_THREAD_SINGLE       0
+#define sc_MPI_THREAD_FUNNELED     1
+#define sc_MPI_THREAD_SERIALIZED   2
+#define sc_MPI_THREAD_MULTIPLE     3
+
+int                 sc_MPI_Init_thread (int *argc, char ***argv,
+                                        int required, int *provided);
+
+#endif /* !(SC_ENABLE_MPI && SC_ENABLE_MPITHREAD) */
+
+/** Return the size of MPI data types.
+ * \param [in] t    MPI data type.
+ * \return          Returns the size in bytes.
+ */
+size_t              sc_mpi_sizeof (sc_MPI_Datatype t);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_MPI_H */
diff --git a/sc/src/sc_notify.c b/sc/src/sc_notify.c
new file mode 100644
index 0000000..0904890
--- /dev/null
+++ b/sc/src/sc_notify.c
@@ -0,0 +1,433 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_containers.h>
+#include <sc_notify.h>
+
+int
+sc_notify_allgather (int *receivers, int num_receivers,
+                     int *senders, int *num_senders, sc_MPI_Comm mpicomm)
+{
+  int                 i, j;
+  int                 found_num_senders;
+  int                 mpiret;
+  int                 mpisize, mpirank;
+  int                 total_num_receivers;
+  int                *procs_num_receivers;
+  int                *offsets_num_receivers;
+  int                *all_receivers;
+
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  procs_num_receivers = SC_ALLOC (int, mpisize);
+  mpiret = sc_MPI_Allgather (&num_receivers, 1, sc_MPI_INT,
+                             procs_num_receivers, 1, sc_MPI_INT, mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  offsets_num_receivers = SC_ALLOC (int, mpisize);
+  total_num_receivers = 0;
+  for (i = 0; i < mpisize; ++i) {
+    offsets_num_receivers[i] = total_num_receivers;
+    total_num_receivers += procs_num_receivers[i];
+  }
+  all_receivers = SC_ALLOC (int, total_num_receivers);
+  mpiret = sc_MPI_Allgatherv (receivers, num_receivers, sc_MPI_INT,
+                              all_receivers, procs_num_receivers,
+                              offsets_num_receivers, sc_MPI_INT, mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  SC_ASSERT (procs_num_receivers[mpirank] == num_receivers);
+  found_num_senders = 0;
+  for (i = 0; i < mpisize; ++i) {
+    for (j = 0; j < procs_num_receivers[i]; ++j) {
+      if (all_receivers[offsets_num_receivers[i] + j] == mpirank) {
+        senders[found_num_senders++] = i;
+        break;
+      }
+    }
+  }
+  *num_senders = found_num_senders;
+  SC_FREE (procs_num_receivers);
+  SC_FREE (offsets_num_receivers);
+  SC_FREE (all_receivers);
+
+  return sc_MPI_SUCCESS;
+}
+
+/** Internally used function to merge two data arrays.
+ * The internal data format of the arrays is as follows:
+ * forall(torank): (torank, howmanyfroms, listoffromranks).
+ * \param [in,out] output   Output array, must initially be empty.
+ * \param [in] input        Input array.  Records torank = -1 are ignored.
+ * \param [in] second       Second input array, valid records only.
+ */
+static void
+sc_notify_merge (sc_array_t * output, sc_array_t * input, sc_array_t * second)
+{
+  int                 i, ir, j, jr, k;
+  int                 torank, numfroms;
+  int                *pint, *psec, *pout;
+
+  SC_ASSERT (input->elem_size == sizeof (int));
+  SC_ASSERT (second->elem_size == sizeof (int));
+  SC_ASSERT (output->elem_size == sizeof (int));
+  SC_ASSERT (output->elem_count == 0);
+
+  i = ir = 0;
+  torank = -1;
+  for (;;) {
+    pint = psec = NULL;
+    while (i < (int) input->elem_count) {
+      /* ignore data that was sent to the peer earlier */
+      pint = (int *) sc_array_index_int (input, i);
+      if (pint[0] == -1) {
+        i += 2 + pint[1];
+        SC_ASSERT (i <= (int) input->elem_count);
+        pint = NULL;
+      }
+      else {
+        break;
+      }
+    }
+    if (ir < (int) second->elem_count) {
+      psec = (int *) sc_array_index_int (second, ir);
+    }
+    if (pint == NULL && psec == NULL) {
+      /* all data is processed and we are done */
+      break;
+    }
+    else if (pint != NULL && psec == NULL) {
+      /* copy input to output */
+    }
+    else if (pint == NULL && psec != NULL) {
+      /* copy second to output */
+    }
+    else {
+      /* both arrays have remaining elements and need to be compared */
+      if (pint[0] < psec[0]) {
+        /* copy input to output */
+        psec = NULL;
+      }
+      else if (pint[0] > psec[0]) {
+        /* copy second to output */
+        pint = NULL;
+      }
+      else {
+        /* both arrays have data for the same processor */
+        SC_ASSERT (torank < pint[0] && pint[0] == psec[0]);
+        torank = pint[0];
+        SC_ASSERT (pint[1] > 0 && psec[1] > 0);
+        SC_ASSERT (i + 2 + pint[1] <= (int) input->elem_count);
+        SC_ASSERT (ir + 2 + psec[1] <= (int) second->elem_count);
+        numfroms = pint[1] + psec[1];
+        pout = (int *) sc_array_push_count (output, 2 + numfroms);
+        pout[0] = torank;
+        pout[1] = numfroms;
+        k = 2;
+        j = jr = 0;
+
+        while (j < pint[1] || jr < psec[1]) {
+          SC_ASSERT (j >= pint[1] || jr >= psec[1]
+                     || pint[2 + j] != psec[2 + jr]);
+          if (j < pint[1] && (jr >= psec[1] || pint[2 + j] < psec[2 + jr])) {
+            pout[k++] = pint[2 + j++];
+          }
+          else {
+            SC_ASSERT (jr < psec[1]);
+            pout[k++] = psec[2 + jr++];
+          }
+        }
+        SC_ASSERT (k == 2 + numfroms);
+        i += 2 + pint[1];
+        ir += 2 + psec[1];
+        continue;
+      }
+    }
+
+    /* we need to copy exactly one buffer to the output array */
+    if (psec == NULL) {
+      SC_ASSERT (pint != NULL);
+      SC_ASSERT (torank < pint[0]);
+      torank = pint[0];
+      SC_ASSERT (i + 2 + pint[1] <= (int) input->elem_count);
+      numfroms = pint[1];
+      SC_ASSERT (numfroms > 0);
+      pout = (int *) sc_array_push_count (output, 2 + numfroms);
+      memcpy (pout, pint, (2 + numfroms) * sizeof (int));
+      i += 2 + numfroms;
+    }
+    else {
+      SC_ASSERT (pint == NULL);
+      SC_ASSERT (torank < psec[0]);
+      torank = psec[0];
+      SC_ASSERT (ir + 2 + psec[1] <= (int) second->elem_count);
+      numfroms = psec[1];
+      SC_ASSERT (numfroms > 0);
+      pout = (int *) sc_array_push_count (output, 2 + numfroms);
+      memcpy (pout, psec, (2 + numfroms) * sizeof (int));
+      ir += 2 + numfroms;
+    }
+  }
+  SC_ASSERT (i == (int) input->elem_count);
+  SC_ASSERT (ir == (int) second->elem_count);
+}
+
+/** Internally used function to execute the sc_notify recursion.
+ * The internal data format of the input and output arrays is as follows:
+ * forall(torank): (torank, howmanyfroms, listoffromranks).
+ * \param [in] mpicomm      Communicator to use.
+ * \param [in] start        Offset of range of ranks.
+ * \param [in] me           Current MPI process in range of ranks.
+ * \param [in] length       Next-biggest or equal power of 2 for range.
+ * \param [in] groupsize    Global count of ranks.
+ * \param [in,out] input    Array of integers.  Overwritten for work space.
+ * \param [in,out] output   Array of integers, must initially be empty.
+ */
+static void
+sc_notify_recursive (sc_MPI_Comm mpicomm, int start, int me, int length,
+                     int groupsize, sc_array_t * input, sc_array_t * output)
+{
+  int                 i;
+  int                 mpiret;
+  int                 num_ta;
+  int                 length2, half;
+  int                 torank, numfroms;
+#ifdef SC_DEBUG
+  int                 j, fromrank, num_out;
+#endif
+  int                 peer, peer2, source;
+  int                 tag, count;
+  int                *pint, *pout;
+  sc_array_t         *temparr, *morebuf;
+  sc_array_t         *sendbuf, *recvbuf;
+  sc_MPI_Request      outrequest;
+  sc_MPI_Status       instatus;
+
+  tag = SC_TAG_NOTIFY_RECURSIVE + SC_LOG2_32 (length);
+  length2 = length / 2;
+  SC_ASSERT (start <= me && me < start + length && me < groupsize);
+  SC_ASSERT (start % length == 0);
+  SC_ASSERT (input->elem_size == sizeof (int));
+  SC_ASSERT (output->elem_size == sizeof (int));
+  SC_ASSERT (output->elem_count == 0);
+
+  if (length > 1) {
+    /* execute recursion */
+    temparr = sc_array_new (sizeof (int));
+    if (me < start + length2) {
+      half = 0;
+      sc_notify_recursive (mpicomm, start, me, length2,
+                           groupsize, input, temparr);
+    }
+    else {
+      half = 1;
+      sc_notify_recursive (mpicomm, start + length2, me, length2,
+                           groupsize, input, temparr);
+    }
+    /* the input array is now invalid and all data is in temparr */
+
+    /* determine communication pattern */
+    peer = me ^ length2;
+    SC_ASSERT (start <= peer && peer < start + length);
+    if (peer < groupsize) {
+      /* peer exists even when mpisize is not a power of 2 */
+      SC_ASSERT ((!half && me < peer) || (half && me > peer));
+    }
+    else {
+      /* peer does not exist; send to a lower processor if nonnegative */
+      SC_ASSERT (!half && me < peer);
+      peer -= length;
+      SC_ASSERT (start - length2 <= peer && peer < start);
+    }
+    peer2 = me + length2;
+    if (half && peer2 < groupsize && (peer2 ^ length2) >= groupsize) {
+      /* we will receive from peer2 who has no peer itself */
+      SC_ASSERT (start + length <= peer2 && !(peer2 & length2));
+    }
+    else {
+      peer2 = -1;
+    }
+    SC_ASSERT (peer >= 0 || peer2 == -1);
+
+    sendbuf = sc_array_new (sizeof (int));
+    if (peer >= 0) {
+      /* send one message */
+      num_ta = (int) temparr->elem_count;
+      torank = -1;
+      for (i = 0; i < num_ta;) {
+        pint = (int *) sc_array_index_int (temparr, i);
+        SC_ASSERT (torank < pint[0]);
+        torank = pint[0];
+        SC_ASSERT (torank % length == me % length ||
+                   torank % length == peer % length);
+        numfroms = pint[1];
+        SC_ASSERT (numfroms > 0);
+        if (torank % length != me % length) {
+          /* this set needs to be sent and is marked invalid in temparr */
+          pout = (int *) sc_array_push_count (sendbuf, 2 + numfroms);
+          memcpy (pout, pint, (2 + numfroms) * sizeof (int));
+          pint[0] = -1;
+        }
+        else {
+          /* this set remains local and valid in temparr */
+        }
+        i += 2 + numfroms;
+      }
+      mpiret = sc_MPI_Isend (sendbuf->array, (int) sendbuf->elem_count,
+                             sc_MPI_INT, peer, tag, mpicomm, &outrequest);
+      SC_CHECK_MPI (mpiret);
+    }
+
+    recvbuf = sc_array_new (sizeof (int));
+    if (peer >= start) {
+      /* receive one message */
+      mpiret = sc_MPI_Probe (sc_MPI_ANY_SOURCE, tag, mpicomm, &instatus);
+      SC_CHECK_MPI (mpiret);
+      source = instatus.MPI_SOURCE;
+      SC_ASSERT (source >= 0 && (source == peer || source == peer2));
+      mpiret = sc_MPI_Get_count (&instatus, sc_MPI_INT, &count);
+      SC_CHECK_MPI (mpiret);
+      sc_array_resize (recvbuf, (size_t) count);
+      mpiret = sc_MPI_Recv (recvbuf->array, count, sc_MPI_INT, source,
+                            tag, mpicomm, sc_MPI_STATUS_IGNORE);
+      SC_CHECK_MPI (mpiret);
+
+      if (peer2 >= 0) {
+        /* merge the temparr and recvbuf arrays */
+        morebuf = sc_array_new (sizeof (int));
+        sc_notify_merge (morebuf, temparr, recvbuf);
+
+        /* receive second message */
+        source = (source == peer2 ? peer : peer2);
+        mpiret = sc_MPI_Probe (source, tag, mpicomm, &instatus);
+        SC_CHECK_MPI (mpiret);
+        mpiret = sc_MPI_Get_count (&instatus, sc_MPI_INT, &count);
+        SC_CHECK_MPI (mpiret);
+        sc_array_resize (recvbuf, (size_t) count);
+        mpiret = sc_MPI_Recv (recvbuf->array, count, sc_MPI_INT, source,
+                              tag, mpicomm, sc_MPI_STATUS_IGNORE);
+        SC_CHECK_MPI (mpiret);
+
+        /* merge the second received array */
+        sc_notify_merge (output, morebuf, recvbuf);
+        sc_array_destroy (morebuf);
+      }
+    }
+    if (peer2 == -1) {
+      sc_notify_merge (output, temparr, recvbuf);
+    }
+    sc_array_destroy (recvbuf);
+    sc_array_destroy (temparr);
+
+    if (peer >= 0) {
+      /* complete send call */
+      mpiret = sc_MPI_Wait (&outrequest, sc_MPI_STATUS_IGNORE);
+      SC_CHECK_MPI (mpiret);
+    }
+    sc_array_destroy (sendbuf);
+  }
+  else {
+    /* end of recursion: copy input to output unchanged */
+    sc_array_copy (output, input);
+  }
+
+#ifdef SC_DEBUG
+  /* verify recursion invariant */
+  num_out = (int) output->elem_count;
+  torank = -1;
+  for (i = 0; i < num_out;) {
+    pint = (int *) sc_array_index_int (output, i);
+    SC_ASSERT (torank < pint[0]);
+    torank = pint[0];
+    SC_ASSERT (torank % length == me % length);
+    numfroms = pint[1];
+    SC_ASSERT (numfroms > 0);
+    fromrank = -1;
+    for (j = 0; j < numfroms; ++j) {
+      SC_ASSERT (fromrank < pint[2 + j]);
+      fromrank = pint[2 + j];
+    }
+    i += 2 + numfroms;
+  }
+#endif
+}
+
+int
+sc_notify (int *receivers, int num_receivers,
+           int *senders, int *num_senders, sc_MPI_Comm mpicomm)
+{
+  int                 i;
+  int                 mpiret;
+  int                 mpisize, mpirank;
+  int                 pow2length;
+  int                 rec;
+  int                 found_num_senders;
+  int                *pint;
+  sc_array_t          input, output;
+
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  pow2length = SC_ROUNDUP2_32 (mpisize);
+  SC_ASSERT (num_receivers >= 0);
+  SC_ASSERT (senders != NULL && num_senders != NULL);
+  SC_ASSERT (pow2length / 2 < mpisize && mpisize <= pow2length);
+
+  sc_array_init (&input, sizeof (int));
+  sc_array_resize (&input, 3 * num_receivers);
+  sc_array_init (&output, sizeof (int));
+  rec = -1;
+  for (i = 0; i < num_receivers; ++i) {
+    SC_ASSERT (rec < receivers[i]);
+    rec = receivers[i];
+    SC_ASSERT (rec < mpisize);
+    pint = (int *) sc_array_index_int (&input, 3 * i);
+    pint[0] = rec;
+    pint[1] = 1;
+    pint[2] = mpirank;
+  }
+
+  sc_notify_recursive (mpicomm, 0, mpirank, pow2length,
+                       mpisize, &input, &output);
+  sc_array_reset (&input);
+
+  found_num_senders = 0;
+  if (output.elem_count > 0) {
+    pint = (int *) sc_array_index_int (&output, 0);
+    SC_ASSERT (pint[0] == mpirank);
+    found_num_senders = pint[1];
+    SC_ASSERT (found_num_senders > 0);
+    SC_ASSERT (output.elem_count == 2 + (size_t) found_num_senders);
+    for (i = 0; i < found_num_senders; ++i) {
+      senders[i] = pint[2 + i];
+    }
+  }
+  *num_senders = found_num_senders;
+  sc_array_reset (&output);
+
+  return sc_MPI_SUCCESS;
+}
diff --git a/sc/src/sc_notify.h b/sc/src/sc_notify.h
new file mode 100644
index 0000000..0fb9bcb
--- /dev/null
+++ b/sc/src/sc_notify.h
@@ -0,0 +1,60 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_NOTIFY_H
+#define SC_NOTIFY_H
+
+#include <sc.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Collective call to notify a set of receiver ranks of current rank.
+ * This version uses one call to sc_MPI_Allgather and one to sc_MPI_Allgatherv.
+ * \see sc_notify
+ * \param [in] receivers        Array of MPI ranks to inform.
+ * \param [in] num_receivers    Count of ranks contained in receivers.
+ * \param [in,out] senders      Array of at least size sc_MPI_Comm_size.
+ *                              On output it contains the notifying ranks.
+ * \param [out] num_senders     On output the number of notifying ranks.
+ * \param [in] mpicomm          MPI communicator to use.
+ * \return                      Aborts on MPI error or returns sc_MPI_SUCCESS.
+ */
+int                 sc_notify_allgather (int *receivers, int num_receivers,
+                                         int *senders, int *num_senders,
+                                         sc_MPI_Comm mpicomm);
+
+/** Collective call to notify a set of receiver ranks of current rank.
+ * \param [in] receivers        Sorted and unique array of MPI ranks to inform.
+ * \param [in] num_receivers    Count of ranks contained in receivers.
+ * \param [in,out] senders      Array of at least size sc_MPI_Comm_size.
+ *                              On output it contains the notifying ranks.
+ * \param [out] num_senders     On output the number of notifying ranks.
+ * \param [in] mpicomm          MPI communicator to use.
+ * \return                      Aborts on MPI error or returns sc_MPI_SUCCESS.
+ */
+int                 sc_notify (int *receivers, int num_receivers,
+                               int *senders, int *num_senders,
+                               sc_MPI_Comm mpicomm);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_NOTIFY_H */
diff --git a/sc/src/sc_obstack.c b/sc/src/sc_obstack.c
new file mode 100644
index 0000000..76e06f0
--- /dev/null
+++ b/sc/src/sc_obstack.c
@@ -0,0 +1,381 @@
+/* *INDENT-OFF* */
+
+/* obstack.c - subroutines used implicitly by object stack macros
+   Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
+   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* renamed from glibc 2.7 to sc_obstack.h and modified */
+
+#include <sc.h>
+
+#ifdef SC_PROVIDE_OBSTACK
+#include "sc_builtin/obstack.h"
+
+/* Determine default alignment.  */
+union fooround
+{
+  uintmax_t i;
+  long double d;
+  void *p;
+};
+struct fooalign
+{
+  char c;
+  union fooround u;
+};
+/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
+   But in fact it might be less smart and round addresses to as much as
+   DEFAULT_ROUNDING.  So we prepare for it to do that.  */
+enum
+  {
+    DEFAULT_ALIGNMENT = offsetof (struct fooalign, u),
+    DEFAULT_ROUNDING = sizeof (union fooround)
+  };
+
+/* When we copy a long block of data, this is the unit to do it with.
+   On some machines, copying successive ints does not work;
+   in such a case, redefine COPYING_UNIT to `long' (if that works)
+   or `char' as a last resort.  */
+# ifndef COPYING_UNIT
+#  define COPYING_UNIT int
+# endif
+
+
+/* The functions allocating more room by calling `obstack_chunk_alloc'
+   jump to the handler pointed to by `obstack_alloc_failed_handler'.
+   This can be set to a user defined function which should either
+   abort gracefully or use longjump - but shouldn't return.  This
+   variable by default points to the internal function
+   `print_and_abort'.  */
+static void print_and_abort (void);
+void (*obstack_alloc_failed_handler) (void) = print_and_abort;
+
+# ifdef _LIBC
+#  if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4)
+/* A looong time ago (before 1994, anyway; we're not sure) this global variable
+   was used by non-GNU-C macros to avoid multiple evaluation.  The GNU C
+   library still exports it because somebody might use it.  */
+struct obstack *_obstack_compat;
+compat_symbol (libc, _obstack_compat, _obstack, GLIBC_2_0);
+#  endif
+# endif
+
+/* Define a macro that either calls functions with the traditional malloc/free
+   calling interface, or calls functions with the mmalloc/mfree interface
+   (that adds an extra first argument), based on the state of use_extra_arg.
+   For free, do not use ?:, since some compilers, like the MIPS compilers,
+   do not allow (expr) ? void : void.  */
+
+# define CALL_CHUNKFUN(h, size) \
+  (((h) -> use_extra_arg) \
+   ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
+   : (*(struct _obstack_chunk *(*) (long)) (h)->chunkfun) ((size)))
+
+# define CALL_FREEFUN(h, old_chunk) \
+  do { \
+    if ((h) -> use_extra_arg) \
+      (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+    else \
+      (*(void (*) (void *)) (h)->freefun) ((old_chunk)); \
+  } while (0)
+
+
+/* Initialize an obstack H for use.  Specify chunk size SIZE (0 means default).
+   Objects start on multiples of ALIGNMENT (0 means use default).
+   CHUNKFUN is the function to use to allocate chunks,
+   and FREEFUN the function to free them.
+
+   Return nonzero if successful, calls obstack_alloc_failed_handler if
+   allocation fails.  */
+
+int
+_obstack_begin (struct obstack *h,
+		int size, int alignment,
+		void *(*chunkfun) (long),
+		void (*freefun) (void *))
+{
+  register struct _obstack_chunk *chunk; /* points to new chunk */
+
+  if (alignment == 0)
+    alignment = DEFAULT_ALIGNMENT;
+  if (size == 0)
+    /* Default size is what GNU malloc can fit in a 4096-byte block.  */
+    {
+      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+	 Use the values for range checking, because if range checking is off,
+	 the extra bytes won't be missed terribly, but if range checking is on
+	 and we used a larger request, a whole extra 4096 bytes would be
+	 allocated.
+
+	 These number are irrelevant to the new GNU malloc.  I suspect it is
+	 less sensitive to the size of the request.  */
+      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+		    + 4 + DEFAULT_ROUNDING - 1)
+		   & ~(DEFAULT_ROUNDING - 1));
+      size = 4096 - extra;
+    }
+
+  h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;
+  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+  h->chunk_size = size;
+  h->alignment_mask = alignment - 1;
+  h->use_extra_arg = 0;
+
+  chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+  if (!chunk)
+    (*obstack_alloc_failed_handler) ();
+  h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
+					       alignment - 1);
+  h->chunk_limit = chunk->limit
+    = (char *) chunk + h->chunk_size;
+  chunk->prev = 0;
+  /* The initial chunk now contains no empty object.  */
+  h->maybe_empty_object = 0;
+  h->alloc_failed = 0;
+  return 1;
+}
+
+int
+_obstack_begin_1 (struct obstack *h, int size, int alignment,
+		  void *(*chunkfun) (void *, long),
+		  void (*freefun) (void *, void *),
+		  void *arg)
+{
+  register struct _obstack_chunk *chunk; /* points to new chunk */
+
+  if (alignment == 0)
+    alignment = DEFAULT_ALIGNMENT;
+  if (size == 0)
+    /* Default size is what GNU malloc can fit in a 4096-byte block.  */
+    {
+      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+	 Use the values for range checking, because if range checking is off,
+	 the extra bytes won't be missed terribly, but if range checking is on
+	 and we used a larger request, a whole extra 4096 bytes would be
+	 allocated.
+
+	 These number are irrelevant to the new GNU malloc.  I suspect it is
+	 less sensitive to the size of the request.  */
+      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+		    + 4 + DEFAULT_ROUNDING - 1)
+		   & ~(DEFAULT_ROUNDING - 1));
+      size = 4096 - extra;
+    }
+
+  h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
+  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+  h->chunk_size = size;
+  h->alignment_mask = alignment - 1;
+  h->extra_arg = arg;
+  h->use_extra_arg = 1;
+
+  chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+  if (!chunk)
+    (*obstack_alloc_failed_handler) ();
+  h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
+					       alignment - 1);
+  h->chunk_limit = chunk->limit
+    = (char *) chunk + h->chunk_size;
+  chunk->prev = 0;
+  /* The initial chunk now contains no empty object.  */
+  h->maybe_empty_object = 0;
+  h->alloc_failed = 0;
+  return 1;
+}
+
+/* Allocate a new current chunk for the obstack *H
+   on the assumption that LENGTH bytes need to be added
+   to the current object, or a new object of length LENGTH allocated.
+   Copies any partial object from the end of the old chunk
+   to the beginning of the new one.  */
+
+void
+_obstack_newchunk (struct obstack *h, int length)
+{
+  register struct _obstack_chunk *old_chunk = h->chunk;
+  register struct _obstack_chunk *new_chunk;
+  register long	new_size;
+  register long obj_size = h->next_free - h->object_base;
+  register long i;
+  long already;
+  char *object_base;
+
+  /* Compute size for new chunk.  */
+  new_size = (obj_size + length) + (obj_size >> 3) + h->alignment_mask + 100;
+  if (new_size < h->chunk_size)
+    new_size = h->chunk_size;
+
+  /* Allocate and initialize the new chunk.  */
+  new_chunk = CALL_CHUNKFUN (h, new_size);
+  if (!new_chunk)
+    (*obstack_alloc_failed_handler) ();
+  h->chunk = new_chunk;
+  new_chunk->prev = old_chunk;
+  new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
+
+  /* Compute an aligned object_base in the new chunk */
+  object_base =
+    __PTR_ALIGN ((char *) new_chunk, new_chunk->contents, h->alignment_mask);
+
+  /* Move the existing object to the new chunk.
+     Word at a time is fast and is safe if the object
+     is sufficiently aligned.  */
+  if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
+    {
+      for (i = obj_size / sizeof (COPYING_UNIT) - 1;
+	   i >= 0; i--)
+	((COPYING_UNIT *)object_base)[i]
+	  = ((COPYING_UNIT *)h->object_base)[i];
+      /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
+	 but that can cross a page boundary on a machine
+	 which does not do strict alignment for COPYING_UNITS.  */
+      already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
+    }
+  else
+    already = 0;
+  /* Copy remaining bytes one by one.  */
+  for (i = already; i < obj_size; i++)
+    object_base[i] = h->object_base[i];
+
+  /* If the object just copied was the only data in OLD_CHUNK,
+     free that chunk and remove it from the chain.
+     But not if that chunk might contain an empty object.  */
+  if (! h->maybe_empty_object
+      && (h->object_base
+	  == __PTR_ALIGN ((char *) old_chunk, old_chunk->contents,
+			  h->alignment_mask)))
+    {
+      new_chunk->prev = old_chunk->prev;
+      CALL_FREEFUN (h, old_chunk);
+    }
+
+  h->object_base = object_base;
+  h->next_free = h->object_base + obj_size;
+  /* The new chunk certainly contains no empty object yet.  */
+  h->maybe_empty_object = 0;
+}
+# ifdef _LIBC
+libc_hidden_def (_obstack_newchunk)
+# endif
+
+/* Return nonzero if object OBJ has been allocated from obstack H.
+   This is here for debugging.
+   If you use it in a program, you are probably losing.  */
+
+/* Suppress -Wmissing-prototypes warning.  We don't want to declare this in
+   obstack.h because it is just for debugging.  */
+int _obstack_allocated_p (struct obstack *h, void *obj);
+
+int
+_obstack_allocated_p (struct obstack *h, void *obj)
+{
+  register struct _obstack_chunk *lp;	/* below addr of any objects in this chunk */
+  register struct _obstack_chunk *plp;	/* point to previous chunk if any */
+
+  lp = (h)->chunk;
+  /* We use >= rather than > since the object cannot be exactly at
+     the beginning of the chunk but might be an empty object exactly
+     at the end of an adjacent chunk.  */
+  while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj))
+    {
+      plp = lp->prev;
+      lp = plp;
+    }
+  return lp != 0;
+}
+
+/* Free objects in obstack H, including OBJ and everything allocate
+   more recently than OBJ.  If OBJ is zero, free everything in H.  */
+
+# undef obstack_free
+
+void
+obstack_free (struct obstack *h, void *obj)
+{
+  register struct _obstack_chunk *lp;	/* below addr of any objects in this chunk */
+  register struct _obstack_chunk *plp;	/* point to previous chunk if any */
+
+  lp = h->chunk;
+  /* We use >= because there cannot be an object at the beginning of a chunk.
+     But there can be an empty object at that address
+     at the end of another chunk.  */
+  while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj))
+    {
+      plp = lp->prev;
+      CALL_FREEFUN (h, lp);
+      lp = plp;
+      /* If we switch chunks, we can't tell whether the new current
+	 chunk contains an empty object, so assume that it may.  */
+      h->maybe_empty_object = 1;
+    }
+  if (lp)
+    {
+      h->object_base = h->next_free = (char *) (obj);
+      h->chunk_limit = lp->limit;
+      h->chunk = lp;
+    }
+  else {
+    /* obj is not in any of the chunks! */
+    SC_CHECK_ABORT (obj == NULL, "Obstack freed invalid object");
+  }
+}
+
+# ifdef _LIBC
+/* Older versions of libc used a function _obstack_free intended to be
+   called by non-GCC compilers.  */
+strong_alias (obstack_free, _obstack_free)
+# endif
+
+int
+_obstack_memory_used (struct obstack *h)
+{
+  register struct _obstack_chunk* lp;
+  register int nbytes = 0;
+
+  for (lp = h->chunk; lp != 0; lp = lp->prev)
+    {
+      nbytes += lp->limit - (char *) lp;
+    }
+  return nbytes;
+}
+
+
+# ifdef _LIBC
+#  include <libio/iolibio.h>
+# endif
+
+# ifndef __attribute__
+/* This feature is available in gcc versions 2.5 and later.  */
+#  if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
+#   define __attribute__(Spec) /* empty */
+#  endif
+# endif
+
+static void
+__attribute__ ((noreturn))
+print_and_abort (void)
+{
+  /* replaced this function */
+
+  SC_CHECK_ABORT (0, "Obstack memory allocation");
+}
+
+#endif /* SC_PROVIDE_OBSTACK */
+
+/* *INDENT-ON* */
diff --git a/sc/src/sc_obstack.h b/sc/src/sc_obstack.h
new file mode 100644
index 0000000..095d2af
--- /dev/null
+++ b/sc/src/sc_obstack.h
@@ -0,0 +1,37 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_OBSTACK_H
+#define SC_OBSTACK_H
+
+#include <sc.h>
+
+#ifdef SC_PROVIDE_OBSTACK
+#ifdef _OBSTACK_H
+#error "obstack.h is included.  Please #include sc.h first."
+#endif
+#include "sc_builtin/obstack.h"
+#else
+#include <obstack.h>
+#endif
+
+#endif /* !SC_OBSTACK_H */
diff --git a/sc/src/sc_options.c b/sc/src/sc_options.c
new file mode 100644
index 0000000..ad83d9e
--- /dev/null
+++ b/sc/src/sc_options.c
@@ -0,0 +1,1202 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_getopt.h>
+#include <sc_options.h>
+#include <iniparser.h>
+
+#include <errno.h>
+
+static char        *sc_iniparser_invalid_key = (char *) -1;
+
+static int
+sc_iniparser_getint (dictionary * d, const char *key, int notfound,
+                     int *iserror)
+{
+  char               *str;
+  long                l;
+
+  str = iniparser_getstring (d, key, sc_iniparser_invalid_key);
+  if (str == sc_iniparser_invalid_key) {
+    return notfound;
+  }
+  l = strtol (str, NULL, 0);
+  if (iserror != NULL) {
+    *iserror = (errno == ERANGE);
+  }
+  if (l < (long) INT_MIN) {
+    if (iserror != NULL) {
+      *iserror = 1;
+    }
+    return INT_MIN;
+  }
+  if (l > (long) INT_MAX) {
+    if (iserror != NULL) {
+      *iserror = 1;
+    }
+    return INT_MAX;
+  }
+  return (int) l;
+}
+
+static              size_t
+sc_iniparser_getsizet (dictionary * d, const char *key, size_t notfound,
+                       int *iserror)
+{
+  char               *str;
+  long long           ll;
+
+  str = iniparser_getstring (d, key, sc_iniparser_invalid_key);
+  if (str == sc_iniparser_invalid_key) {
+    return notfound;
+  }
+#ifndef SC_HAVE_STRTOLL
+  ll = (long long) strtol (str, NULL, 0);
+#else
+  ll = strtoll (str, NULL, 0);
+#endif
+  if (iserror != NULL) {
+    *iserror = (errno == ERANGE);
+  }
+  if (ll < 0LL) {
+    if (iserror != NULL) {
+      *iserror = 1;
+    }
+    return 0;
+  }
+  return (size_t) ll;
+}
+
+static double
+sc_iniparser_getdouble (dictionary * d, const char *key, double notfound,
+                        int *iserror)
+{
+  char               *str;
+  double              dbl;
+
+  str = iniparser_getstring (d, key, sc_iniparser_invalid_key);
+  if (str == sc_iniparser_invalid_key) {
+    return notfound;
+  }
+  dbl = strtod (str, NULL);
+  if (iserror != NULL) {
+    *iserror = (errno == ERANGE);
+  }
+  return dbl;
+}
+
+static void
+sc_options_free_args (sc_options_t * opt)
+{
+  int                 i;
+
+  if (opt->args_alloced) {
+    SC_ASSERT (opt->first_arg == 0);
+    for (i = 0; i < opt->argc; ++i) {
+      SC_FREE (opt->argv[i]);
+    }
+    SC_FREE (opt->argv);
+  }
+
+  opt->args_alloced = 0;
+  opt->first_arg = 0;
+  opt->argc = 0;
+  opt->argv = NULL;
+}
+
+sc_options_t       *
+sc_options_new (const char *program_path)
+{
+  sc_options_t       *opt;
+
+  opt = SC_ALLOC_ZERO (sc_options_t, 1);
+
+  snprintf (opt->program_path, BUFSIZ, "%s", program_path);
+  opt->program_name = basename (opt->program_path);
+  opt->option_items = sc_array_new (sizeof (sc_option_item_t));
+  opt->subopt_names = sc_array_new (sizeof (char *));
+  opt->args_alloced = 0;
+  opt->first_arg = -1;
+  opt->argc = 0;
+  opt->argv = NULL;
+
+  return opt;
+}
+
+void
+sc_options_destroy (sc_options_t * opt)
+{
+  size_t              iz;
+  sc_array_t         *items = opt->option_items;
+  size_t              count = items->elem_count;
+  sc_option_item_t   *item;
+  sc_array_t         *names = opt->subopt_names;
+  char               *name;
+
+  for (iz = 0; iz < count; ++iz) {
+    item = (sc_option_item_t *) sc_array_index (items, iz);
+    SC_FREE (item->string_value);
+  }
+
+  sc_options_free_args (opt);
+  sc_array_destroy (opt->option_items);
+
+  count = names->elem_count;
+  for (iz = 0; iz < count; iz++) {
+    name = *((char **) sc_array_index (names, iz));
+    SC_FREE (name);
+  }
+  sc_array_destroy (opt->subopt_names);
+
+  SC_FREE (opt);
+}
+
+void
+sc_options_add_switch (sc_options_t * opt, int opt_char,
+                       const char *opt_name,
+                       int *variable, const char *help_string)
+{
+  sc_option_item_t   *item;
+
+  SC_ASSERT (opt_char != '\0' || opt_name != NULL);
+  SC_ASSERT (opt_name == NULL || opt_name[0] != '-');
+
+  item = (sc_option_item_t *) sc_array_push (opt->option_items);
+
+  item->opt_type = SC_OPTION_SWITCH;
+  item->opt_char = opt_char;
+  item->opt_name = opt_name;
+  item->opt_var = variable;
+  item->opt_fn = NULL;
+  item->has_arg = 0;
+  item->called = 0;
+  item->help_string = help_string;
+  item->string_value = NULL;
+  item->user_data = NULL;
+
+  *variable = 0;
+}
+
+void
+sc_options_add_bool (sc_options_t * opt, int opt_char,
+                     const char *opt_name,
+                     int *variable, int init_value, const char *help_string)
+{
+  sc_option_item_t   *item;
+
+  SC_ASSERT (opt_char != '\0' || opt_name != NULL);
+  SC_ASSERT (opt_name == NULL || opt_name[0] != '-');
+
+  item = (sc_option_item_t *) sc_array_push (opt->option_items);
+
+  item->opt_type = SC_OPTION_BOOL;
+  item->opt_char = opt_char;
+  item->opt_name = opt_name;
+  item->opt_var = variable;
+  item->opt_fn = NULL;
+  item->has_arg = 2;
+  item->called = 0;
+  item->help_string = help_string;
+  item->string_value = NULL;
+  item->user_data = NULL;
+
+  *variable = init_value;
+}
+
+void
+sc_options_add_int (sc_options_t * opt, int opt_char, const char *opt_name,
+                    int *variable, int init_value, const char *help_string)
+{
+  sc_option_item_t   *item;
+
+  SC_ASSERT (opt_char != '\0' || opt_name != NULL);
+  SC_ASSERT (opt_name == NULL || opt_name[0] != '-');
+
+  item = (sc_option_item_t *) sc_array_push (opt->option_items);
+
+  item->opt_type = SC_OPTION_INT;
+  item->opt_char = opt_char;
+  item->opt_name = opt_name;
+  item->opt_var = variable;
+  item->opt_fn = NULL;
+  item->has_arg = 1;
+  item->called = 0;
+  item->help_string = help_string;
+  item->string_value = NULL;
+  item->user_data = NULL;
+
+  *variable = init_value;
+}
+
+void
+sc_options_add_size_t (sc_options_t * opt, int opt_char, const char *opt_name,
+                       size_t * variable, size_t init_value,
+                       const char *help_string)
+{
+  sc_option_item_t   *item;
+
+  SC_ASSERT (opt_char != '\0' || opt_name != NULL);
+  SC_ASSERT (opt_name == NULL || opt_name[0] != '-');
+
+  item = (sc_option_item_t *) sc_array_push (opt->option_items);
+
+  item->opt_type = SC_OPTION_SIZE_T;
+  item->opt_char = opt_char;
+  item->opt_name = opt_name;
+  item->opt_var = variable;
+  item->opt_fn = NULL;
+  item->has_arg = 1;
+  item->called = 0;
+  item->help_string = help_string;
+  item->string_value = NULL;
+  item->user_data = NULL;
+
+  *variable = init_value;
+}
+
+void
+sc_options_add_double (sc_options_t * opt, int opt_char,
+                       const char *opt_name,
+                       double *variable, double init_value,
+                       const char *help_string)
+{
+  sc_option_item_t   *item;
+
+  SC_ASSERT (opt_char != '\0' || opt_name != NULL);
+  SC_ASSERT (opt_name == NULL || opt_name[0] != '-');
+
+  item = (sc_option_item_t *) sc_array_push (opt->option_items);
+
+  item->opt_type = SC_OPTION_DOUBLE;
+  item->opt_char = opt_char;
+  item->opt_name = opt_name;
+  item->opt_var = variable;
+  item->opt_fn = NULL;
+  item->has_arg = 1;
+  item->called = 0;
+  item->help_string = help_string;
+  item->string_value = NULL;
+  item->user_data = NULL;
+
+  *variable = init_value;
+}
+
+void
+sc_options_add_string (sc_options_t * opt, int opt_char,
+                       const char *opt_name, const char **variable,
+                       const char *init_value, const char *help_string)
+{
+  sc_option_item_t   *item;
+
+  SC_ASSERT (opt_char != '\0' || opt_name != NULL);
+  SC_ASSERT (opt_name == NULL || opt_name[0] != '-');
+
+  item = (sc_option_item_t *) sc_array_push (opt->option_items);
+
+  item->opt_type = SC_OPTION_STRING;
+  item->opt_char = opt_char;
+  item->opt_name = opt_name;
+  item->opt_var = variable;
+  item->opt_fn = NULL;
+  item->has_arg = 1;
+  item->called = 0;
+  item->help_string = help_string;
+  item->user_data = NULL;
+
+  /* init_value may be NULL */
+  *variable = item->string_value = SC_STRDUP (init_value);
+}
+
+void
+sc_options_add_inifile (sc_options_t * opt, int opt_char,
+                        const char *opt_name, const char *help_string)
+{
+  sc_option_item_t   *item;
+
+  SC_ASSERT (opt_char != '\0' || opt_name != NULL);
+  SC_ASSERT (opt_name == NULL || opt_name[0] != '-');
+
+  item = (sc_option_item_t *) sc_array_push (opt->option_items);
+
+  item->opt_type = SC_OPTION_INIFILE;
+  item->opt_char = opt_char;
+  item->opt_name = opt_name;
+  item->opt_var = NULL;
+  item->opt_fn = NULL;
+  item->has_arg = 1;
+  item->called = 0;
+  item->help_string = help_string;
+  item->string_value = NULL;
+  item->user_data = NULL;
+}
+
+void
+sc_options_add_callback (sc_options_t * opt, int opt_char,
+                         const char *opt_name, int has_arg,
+                         sc_options_callback_t fn, void *data,
+                         const char *help_string)
+{
+  sc_option_item_t   *item;
+
+  SC_ASSERT (opt_char != '\0' || opt_name != NULL);
+  SC_ASSERT (opt_name == NULL || opt_name[0] != '-');
+
+  item = (sc_option_item_t *) sc_array_push (opt->option_items);
+
+  item->opt_type = SC_OPTION_CALLBACK;
+  item->opt_char = opt_char;
+  item->opt_name = opt_name;
+  item->opt_var = NULL;
+  item->opt_fn = (void (*)(void)) fn;
+  item->has_arg = has_arg;
+  item->called = 0;
+  item->help_string = help_string;
+  item->string_value = NULL;
+  item->user_data = data;
+}
+
+void
+sc_options_add_suboptions (sc_options_t * opt,
+                           sc_options_t * subopt, const char *prefix)
+{
+  sc_array_t         *subopt_names = opt->subopt_names;
+  sc_array_t         *items = subopt->option_items;
+  size_t              count = items->elem_count;
+  sc_option_item_t   *item;
+  size_t              iz;
+  int                 prefixlen = strlen (prefix);
+  int                 namelen;
+  char              **name;
+
+  for (iz = 0; iz < count; iz++) {
+    item = (sc_option_item_t *) sc_array_index (items, iz);
+
+    namelen = prefixlen +
+      ((item->opt_name != NULL) ? (strlen (item->opt_name) + 2) : 4);
+    name = (char **) sc_array_push (subopt_names);
+    *name = SC_ALLOC (char, namelen);
+    if (item->opt_name != NULL) {
+      snprintf (*name, namelen, "%s:%s", prefix, item->opt_name);
+    }
+    else {
+      snprintf (*name, namelen, "%s:-%c", prefix, item->opt_char);
+    }
+
+    switch (item->opt_type) {
+    case SC_OPTION_SWITCH:
+      sc_options_add_switch (opt, '\0', *name, (int *) item->opt_var,
+                             item->help_string);
+      break;
+    case SC_OPTION_BOOL:
+      sc_options_add_bool (opt, '\0', *name, (int *) item->opt_var,
+                           *((int *) item->opt_var), item->help_string);
+      break;
+    case SC_OPTION_INT:
+      sc_options_add_int (opt, '\0', *name, (int *) item->opt_var,
+                          *((int *) item->opt_var), item->help_string);
+      break;
+    case SC_OPTION_SIZE_T:
+      sc_options_add_size_t (opt, '\0', *name, (size_t *) item->opt_var,
+                             *((size_t *) item->opt_var), item->help_string);
+      break;
+    case SC_OPTION_DOUBLE:
+      sc_options_add_double (opt, '\0', *name, (double *) item->opt_var,
+                             *((double *) item->opt_var), item->help_string);
+      break;
+    case SC_OPTION_STRING:
+      sc_options_add_string (opt, '\0', *name, (const char **) item->opt_var,
+                             item->string_value, item->help_string);
+      break;
+    case SC_OPTION_INIFILE:
+      sc_options_add_inifile (opt, '\0', *name, item->help_string);
+      break;
+    case SC_OPTION_CALLBACK:
+      sc_options_add_callback (opt, '\0', *name, item->has_arg,
+                               (sc_options_callback_t) item->opt_fn,
+                               item->user_data, item->help_string);
+      break;
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+  }
+}
+
+void
+sc_options_print_usage (int package_id, int log_priority,
+                        sc_options_t * opt, const char *arg_usage)
+{
+  int                 printed;
+  size_t              iz;
+  sc_array_t         *items = opt->option_items;
+  size_t              count = items->elem_count;
+  sc_option_item_t   *item;
+  const char         *provide_short;
+  const char         *provide_long;
+  char                outbuf[BUFSIZ];
+  char               *copy, *tok;
+
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+               "Usage: %s%s%s\n", opt->program_name,
+               count == 0 ? "" : " <OPTIONS>",
+               arg_usage == NULL ? "" : " <ARGUMENTS>");
+  if (count > 0) {
+    SC_GEN_LOG (package_id, SC_LC_GLOBAL, log_priority, "Options:\n");
+  }
+
+  for (iz = 0; iz < count; ++iz) {
+    item = (sc_option_item_t *) sc_array_index (items, iz);
+    provide_short = "";
+    provide_long = "";
+    switch (item->opt_type) {
+    case SC_OPTION_SWITCH:
+      break;
+    case SC_OPTION_BOOL:
+      provide_short = " [0fFnN1tTyY]";
+      provide_long = "[=0fFnN1tTyY]";
+      break;
+    case SC_OPTION_INT:
+      provide_short = " <INT>";
+      provide_long = "=<INT>";
+      break;
+    case SC_OPTION_SIZE_T:
+      provide_short = " <SIZE_T>";
+      provide_long = "=<SIZE_T>";
+      break;
+    case SC_OPTION_DOUBLE:
+      provide_short = " <REAL>";
+      provide_long = "=<REAL>";
+      break;
+    case SC_OPTION_STRING:
+      provide_short = " <STRING>";
+      provide_long = "=<STRING>";
+      break;
+    case SC_OPTION_INIFILE:
+      provide_short = " <INIFILE>";
+      provide_long = "=<INIFILE>";
+      break;
+    case SC_OPTION_CALLBACK:
+      if (item->has_arg) {
+        provide_short = " <ARG>";
+        provide_long = "=<ARG>";
+      }
+      break;
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+    outbuf[0] = '\0';
+    printed = 0;
+    if (item->opt_char != '\0' && item->opt_name != NULL) {
+      printed = snprintf (outbuf, BUFSIZ, "   -%c%s | --%s%s",
+                          item->opt_char, provide_short,
+                          item->opt_name, provide_long);
+    }
+    else if (item->opt_char != '\0') {
+      printed = snprintf (outbuf, BUFSIZ, "   -%c%s",
+                          item->opt_char, provide_short);
+    }
+    else if (item->opt_name != NULL) {
+      printed = snprintf (outbuf, BUFSIZ, "   --%s%s",
+                          item->opt_name, provide_long);
+    }
+    else {
+      SC_ABORT_NOT_REACHED ();
+    }
+    if (item->help_string != NULL) {
+      snprintf (outbuf + printed, BUFSIZ - printed, "%*s%s",
+                SC_MAX (1, 40 - printed), "", item->help_string);
+    }
+    SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority, "%s\n", outbuf);
+  }
+
+  if (arg_usage != NULL && arg_usage[0] != '\0') {
+    SC_GEN_LOG (package_id, SC_LC_GLOBAL, log_priority, "Arguments:\n");
+    copy = SC_STRDUP (arg_usage);
+    for (tok = strtok (copy, "\n\r"); tok != NULL;
+         tok = strtok (NULL, "\n\r")) {
+      SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority, "   %s\n", tok);
+    }
+    SC_FREE (copy);
+  }
+}
+
+void
+sc_options_print_summary (int package_id, int log_priority,
+                          sc_options_t * opt)
+{
+  int                 i;
+  int                 bvalue, printed;
+  size_t              iz;
+  sc_array_t         *items = opt->option_items;
+  size_t              count = items->elem_count;
+  sc_option_item_t   *item;
+  const char         *string_val;
+  char                outbuf[BUFSIZ];
+
+  SC_GEN_LOG (package_id, SC_LC_GLOBAL, log_priority, "Options:\n");
+
+  for (iz = 0; iz < count; ++iz) {
+    item = (sc_option_item_t *) sc_array_index (items, iz);
+    if (item->opt_type == SC_OPTION_INIFILE) {
+      continue;
+    }
+    if (item->opt_name == NULL) {
+      printed = snprintf (outbuf, BUFSIZ, "   -%c: ", item->opt_char);
+    }
+    else {
+      printed = snprintf (outbuf, BUFSIZ, "   %s: ", item->opt_name);
+    }
+    switch (item->opt_type) {
+    case SC_OPTION_SWITCH:
+      bvalue = *(int *) item->opt_var;
+      if (bvalue <= 1)
+        printed += snprintf (outbuf + printed, BUFSIZ - printed,
+                             "%s", bvalue ? "true" : "false");
+      else
+        printed += snprintf (outbuf + printed, BUFSIZ - printed,
+                             "%d", bvalue);
+      break;
+    case SC_OPTION_BOOL:
+      printed += snprintf (outbuf + printed, BUFSIZ - printed,
+                           "%s", *(int *) item->opt_var ? "true" : "false");
+      break;
+    case SC_OPTION_INT:
+      printed += snprintf (outbuf + printed, BUFSIZ - printed,
+                           "%d", *(int *) item->opt_var);
+      break;
+    case SC_OPTION_SIZE_T:
+      printed += snprintf (outbuf + printed, BUFSIZ - printed, "%llu",
+                           (unsigned long long) *(size_t *) item->opt_var);
+      break;
+    case SC_OPTION_DOUBLE:
+      printed += snprintf (outbuf + printed, BUFSIZ - printed,
+                           "%g", *(double *) item->opt_var);
+      break;
+    case SC_OPTION_STRING:
+      string_val = *(const char **) item->opt_var;
+      if (string_val == NULL) {
+        string_val = "<unspecified>";
+      }
+      printed += snprintf (outbuf + printed, BUFSIZ - printed,
+                           "%s", string_val);
+      break;
+    case SC_OPTION_CALLBACK:
+      if (item->called) {
+        string_val = item->has_arg ? item->string_value : "true";
+      }
+      else {
+        string_val = "<unspecified>";
+      }
+      printed += snprintf (outbuf + printed, BUFSIZ - printed,
+                           "%s", string_val);
+      break;
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+    SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority, "%s\n", outbuf);
+  }
+
+  if (opt->first_arg < 0) {
+    SC_GEN_LOG (package_id, SC_LC_GLOBAL, log_priority,
+                "Arguments: not parsed\n");
+  }
+  else {
+    if (opt->first_arg == opt->argc) {
+      SC_GEN_LOG (package_id, SC_LC_GLOBAL, log_priority,
+                  "Arguments: none\n");
+    }
+    else {
+      SC_GEN_LOG (package_id, SC_LC_GLOBAL, log_priority, "Arguments:\n");
+    }
+    for (i = opt->first_arg; i < opt->argc; ++i) {
+      SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority, "   %d: %s\n",
+                   i - opt->first_arg, opt->argv[i]);
+    }
+  }
+}
+
+int
+sc_options_load (int package_id, int err_priority,
+                 sc_options_t * opt, const char *inifile)
+{
+  int                 found_short, found_long;
+  size_t              iz;
+  sc_array_t         *items = opt->option_items;
+  size_t              count = items->elem_count;
+  sc_option_item_t   *item;
+  dictionary         *dict;
+  int                 iserror;
+  int                 bvalue;
+  int                *ivalue;
+  double             *dvalue;
+  size_t             *zvalue;
+  const char         *s, *key;
+  char                skey[BUFSIZ], lkey[BUFSIZ];
+  sc_options_callback_t fn;
+
+  dict = iniparser_load (inifile);
+  if (dict == NULL) {
+    SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                "Could not load or parse inifile\n");
+    return -1;
+  }
+
+  for (iz = 0; iz < count; ++iz) {
+    item = (sc_option_item_t *) sc_array_index (items, iz);
+    if (item->opt_type == SC_OPTION_INIFILE) {
+      continue;
+    }
+
+    key = NULL;
+    skey[0] = lkey[0] = '\0';
+    found_short = found_long = 0;
+    if (item->opt_char != '\0') {
+      snprintf (skey, BUFSIZ, "Options:-%c", item->opt_char);
+      found_short = iniparser_find_entry (dict, skey);
+    }
+    if (item->opt_name != NULL) {
+      /* if the name contains a section prefix, don't add "Options:" */
+      if (strchr (item->opt_name, ':') != NULL) {
+        SC_ASSERT (item->opt_char == '\0');
+        snprintf (lkey, BUFSIZ, "%s", item->opt_name);
+      }
+      else {
+        snprintf (lkey, BUFSIZ, "Options:%s", item->opt_name);
+      }
+      found_long = iniparser_find_entry (dict, lkey);
+    }
+    if (found_short && found_long) {
+      SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                   "Duplicates %s %s in file: %s\n", skey, lkey, inifile);
+      iniparser_freedict (dict);
+      return -1;
+    }
+    else if (found_long) {
+      key = lkey;
+    }
+    else if (found_short) {
+      key = skey;
+    }
+    else {
+      continue;
+    }
+
+    ++item->called;
+    switch (item->opt_type) {
+    case SC_OPTION_SWITCH:
+      bvalue = iniparser_getboolean (dict, key, -1);
+      if (bvalue == -1) {
+        bvalue = sc_iniparser_getint (dict, key, 0, &iserror);
+        if (bvalue <= 0 || iserror) {
+          SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                       "Invalid switch %s in file: %s\n", key, inifile);
+          iniparser_freedict (dict);
+          return -1;
+        }
+      }
+      *(int *) item->opt_var = bvalue;
+      break;
+    case SC_OPTION_BOOL:
+      bvalue = iniparser_getboolean (dict, key, -1);
+      if (bvalue == -1) {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Invalid boolean %s in file: %s\n", key, inifile);
+        iniparser_freedict (dict);
+        return -1;
+      }
+      *(int *) item->opt_var = bvalue;
+      break;
+    case SC_OPTION_INT:
+      ivalue = (int *) item->opt_var;
+      *ivalue = sc_iniparser_getint (dict, key, *ivalue, &iserror);
+      if (iserror) {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Invalid int %s in file: %s\n", key, inifile);
+        iniparser_freedict (dict);
+        return -1;
+      }
+      break;
+    case SC_OPTION_SIZE_T:
+      zvalue = (size_t *) item->opt_var;
+      *zvalue = sc_iniparser_getsizet (dict, key, *zvalue, &iserror);
+      if (iserror) {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Invalid size_t %s in file: %s\n", key, inifile);
+        iniparser_freedict (dict);
+        return -1;
+      }
+      break;
+    case SC_OPTION_DOUBLE:
+      dvalue = (double *) item->opt_var;
+      *dvalue = sc_iniparser_getdouble (dict, key, *dvalue, &iserror);
+      if (iserror) {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Invalid double %s in file: %s\n", key, inifile);
+        iniparser_freedict (dict);
+        return -1;
+      }
+      break;
+    case SC_OPTION_STRING:
+      s = iniparser_getstring (dict, key, NULL);
+      if (s != NULL) {
+        SC_FREE (item->string_value);   /* deals with NULL */
+        *(const char **) item->opt_var = item->string_value = SC_STRDUP (s);
+      }
+      break;
+    case SC_OPTION_CALLBACK:
+      if (item->has_arg) {
+        s = iniparser_getstring (dict, key, NULL);
+        if (s == NULL) {
+          SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                       "Invalid string %s in file: %s\n", key, inifile);
+          iniparser_freedict (dict);
+          return -1;
+        }
+        SC_FREE (item->string_value);   /* deals with NULL */
+        item->string_value = SC_STRDUP (s);
+      }
+      else {
+        s = NULL;
+      }
+      fn = (sc_options_callback_t) item->opt_fn;
+      if (fn (opt, s, item->user_data)) {
+        iniparser_freedict (dict);
+        return -1;
+      }
+      break;
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+  }
+
+  iniparser_freedict (dict);
+  return 0;
+}
+
+int
+sc_options_save (int package_id, int err_priority,
+                 sc_options_t * opt, const char *inifile)
+{
+  int                 retval;
+  int                 i;
+  int                 bvalue;
+  size_t              iz;
+  sc_array_t         *items = opt->option_items;
+  size_t              count = items->elem_count;
+  sc_option_item_t   *item;
+  FILE               *file;
+  const char         *default_prefix = "Options";
+  const char         *last_prefix;
+  const char         *this_prefix;
+  const char         *base_name;
+  size_t              last_n;
+  size_t              this_n;
+
+  /* this routine must only be called after successful option parsing */
+  SC_ASSERT (opt->argc >= 0 && opt->first_arg >= 0);
+  SC_ASSERT (opt->first_arg <= opt->argc);
+
+  file = fopen (inifile, "wb");
+  if (file == NULL) {
+    SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority, "File open failed\n");
+    return -1;
+  }
+
+  retval = fprintf (file, "# written by sc_options_save\n");
+  if (retval < 0) {
+    SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                "Write title 1 failed\n");
+    fclose (file);
+    return -1;
+  }
+
+  this_prefix = NULL;
+  last_prefix = NULL;
+  this_n = last_n = 0;
+
+  for (iz = 0; iz < count; ++iz) {
+    item = (sc_option_item_t *) sc_array_index (items, iz);
+    if (item->opt_type == SC_OPTION_STRING && item->string_value == NULL) {
+      continue;
+    }
+    if (item->opt_type == SC_OPTION_INIFILE) {
+      continue;
+    }
+    if (item->opt_type == SC_OPTION_CALLBACK && !item->called) {
+      continue;
+    }
+
+    base_name = NULL;
+    if (item->opt_name != NULL) {
+      this_prefix = strrchr (item->opt_name, ':');
+      if (this_prefix == NULL) {
+        base_name = item->opt_name;
+        this_prefix = default_prefix;
+        this_n = strlen (default_prefix);
+      }
+      else {
+        /* base name is whatever is to the right of the last colon */
+        base_name = this_prefix + 1;
+        this_n = this_prefix - item->opt_name;
+        this_prefix = item->opt_name;
+      }
+    }
+
+    if (this_prefix != NULL &&
+        (last_prefix == NULL || this_n != last_n ||
+         strncmp (this_prefix, last_prefix, this_n) != 0)) {
+      retval = fprintf (file, "[%.*s]\n", (int) this_n, this_prefix);
+      if (retval < 0) {
+        SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                    "Write section heading failed\n");
+        fclose (file);
+        return -1;
+      }
+      last_prefix = this_prefix;
+      last_n = this_n;
+    }
+
+    retval = 0;
+    if (base_name != NULL) {
+      retval = fprintf (file, "        %s = ", base_name);
+    }
+    else if (item->opt_char != '\0') {
+      retval = fprintf (file, "        -%c = ", item->opt_char);
+    }
+    else {
+      SC_ABORT_NOT_REACHED ();
+    }
+    if (retval < 0) {
+      SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                  "Write key failed\n");
+      fclose (file);
+      return -1;
+    }
+
+    retval = 0;
+    switch (item->opt_type) {
+    case SC_OPTION_SWITCH:
+      bvalue = *(int *) item->opt_var;
+      if (bvalue <= 1)
+        retval = fprintf (file, "%s\n", bvalue ? "true" : "false");
+      else
+        retval = fprintf (file, "%d\n", bvalue);
+      break;
+    case SC_OPTION_BOOL:
+      retval = fprintf (file, "%s\n",
+                        *(int *) item->opt_var ? "true" : "false");
+      break;
+    case SC_OPTION_INT:
+      retval = fprintf (file, "%d\n", *(int *) item->opt_var);
+      break;
+    case SC_OPTION_SIZE_T:
+      retval = fprintf (file, "%llu\n",
+                        (unsigned long long) *(size_t *) item->opt_var);
+      break;
+    case SC_OPTION_DOUBLE:
+      retval = fprintf (file, "%.16g\n", *(double *) item->opt_var);
+      break;
+    case SC_OPTION_STRING:
+      retval = fprintf (file, "%s\n", item->string_value);
+      break;
+    case SC_OPTION_CALLBACK:
+      if (item->has_arg) {
+        SC_ASSERT (item->string_value != NULL);
+        retval = fprintf (file, "%s\n", item->string_value);
+      }
+      else {
+        retval = fprintf (file, "%s\n", "true");
+      }
+      break;
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+    if (retval < 0) {
+      SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                  "Write value failed\n");
+      fclose (file);
+      return -1;
+    }
+  }
+
+  retval = fprintf (file, "[Arguments]\n        count = %d\n",
+                    opt->argc - opt->first_arg);
+  if (retval < 0) {
+    SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                "Write title 2 failed\n");
+    fclose (file);
+    return -1;
+  }
+  for (i = opt->first_arg; i < opt->argc; ++i) {
+    retval = fprintf (file, "        %d = %s\n",
+                      i - opt->first_arg, opt->argv[i]);
+    if (retval < 0) {
+      SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                  "Write argument failed\n");
+      fclose (file);
+      return -1;
+    }
+  }
+
+  retval = fclose (file);
+  if (retval) {
+    SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                "File close failed\n");
+    return -1;
+  }
+
+  return 0;
+}
+
+int
+sc_options_parse (int package_id, int err_priority, sc_options_t * opt,
+                  int argc, char **argv)
+{
+  int                 retval;
+  int                 position, printed;
+  int                 c, option_index;
+  int                 item_index = -1;
+  size_t              iz;
+  long                ilong;
+  long long           ilonglong;
+  double              dbl;
+  sc_array_t         *items = opt->option_items;
+  size_t              count = items->elem_count;
+  sc_option_item_t   *item;
+  char                optstring[BUFSIZ];
+  struct option      *longopts, *lo;
+  sc_options_callback_t fn;
+
+  /* build getopt string and long option structures */
+
+  longopts = SC_ALLOC_ZERO (struct option, count + 1);
+
+  lo = longopts;
+  position = 0;
+  for (iz = 0; iz < count; ++iz) {
+    item = (sc_option_item_t *) sc_array_index (items, iz);
+    if (item->opt_char != '\0') {
+      printed = snprintf (optstring + position, BUFSIZ - position,
+                          "%c%s", item->opt_char, item->has_arg ?
+                          item->has_arg == 2 ? "::" : ":" : "");
+      SC_ASSERT (printed > 0);
+      position += printed;
+    }
+    if (item->opt_name != NULL) {
+      lo->name = item->opt_name;
+      lo->has_arg = item->has_arg;
+      lo->flag = &item_index;
+      lo->val = (int) iz;
+      ++lo;
+    }
+  }
+
+  /* run getopt_long loop */
+
+  retval = 0;
+  opterr = 0;
+  while (retval == 0) {
+    c = getopt_long (argc, argv, optstring, longopts, &option_index);
+    if (c == -1) {
+      break;
+    }
+    if (c == '?') {             /* invalid option */
+      if (optopt == 0) {
+        SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                    "Encountered invalid long option\n");
+      }
+      else {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Encountered invalid short option: -%c\n", optopt);
+      }
+      retval = -1;
+      break;
+    }
+
+    item = NULL;
+    if (c == 0) {               /* long option */
+      SC_ASSERT (item_index >= 0);
+      item = (sc_option_item_t *) sc_array_index (items, (size_t) item_index);
+    }
+    else {                      /* short option */
+      for (iz = 0; iz < count; ++iz) {
+        item = (sc_option_item_t *) sc_array_index (items, iz);
+        if (item->opt_char == c) {
+          break;
+        }
+      }
+      if (iz == count) {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Encountered invalid short option: -%c\n", c);
+        retval = -1;
+        break;
+      }
+    }
+    SC_ASSERT (item != NULL);
+
+    ++item->called;
+    switch (item->opt_type) {
+    case SC_OPTION_SWITCH:
+      ++*(int *) item->opt_var;
+      break;
+    case SC_OPTION_BOOL:
+      if (optarg == NULL) {
+        *(int *) item->opt_var = 1;
+      }
+      else if (strspn (optarg, "1tTyY") > 0) {
+        *(int *) item->opt_var = 1;
+      }
+      else if (strspn (optarg, "0fFnN") > 0) {
+        *(int *) item->opt_var = 0;
+      }
+      else {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Error parsing boolean: %s\n", optarg);
+        retval = -1;            /* this ends option processing */
+      }
+      break;
+    case SC_OPTION_INT:
+      ilong = strtol (optarg, NULL, 0);
+      if (ilong < (long) INT_MIN || ilong > (long) INT_MAX || errno == ERANGE) {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Error parsing int: %s\n", optarg);
+        retval = -1;            /* this ends option processing */
+      }
+      else {
+        *(int *) item->opt_var = (int) ilong;
+      }
+      break;
+    case SC_OPTION_SIZE_T:
+#ifndef SC_HAVE_STRTOLL
+      ilonglong = (long long) strtol (optarg, NULL, 0);
+#else
+      ilonglong = strtoll (optarg, NULL, 0);
+#endif
+      if (ilonglong < 0LL || errno == ERANGE) {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Error parsing size_t: %s\n", optarg);
+        retval = -1;            /* this ends option processing */
+      }
+      else {
+        *(size_t *) item->opt_var = (size_t) ilonglong;
+      }
+      break;
+    case SC_OPTION_DOUBLE:
+      dbl = strtod (optarg, NULL);
+      if (errno == ERANGE) {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Error parsing double: %s\n", optarg);
+        retval = -1;            /* this ends option processing */
+      }
+      else {
+        *(double *) item->opt_var = dbl;
+      }
+      break;
+    case SC_OPTION_STRING:
+      SC_FREE (item->string_value);     /* deals with NULL */
+      *(const char **) item->opt_var = item->string_value =
+        SC_STRDUP (optarg);
+      break;
+    case SC_OPTION_INIFILE:
+      if (sc_options_load (package_id, err_priority, opt, optarg)) {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, err_priority,
+                     "Error loading file: %s\n", optarg);
+        retval = -1;            /* this ends option processing */
+      }
+      break;
+    case SC_OPTION_CALLBACK:
+      if (item->has_arg) {
+        SC_FREE (item->string_value);   /* deals with NULL */
+        item->string_value = SC_STRDUP (optarg);
+      }
+      fn = (sc_options_callback_t) item->opt_fn;
+      if (fn (opt, item->has_arg ? optarg : NULL, item->user_data)) {
+#if 0
+        SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                    "Error in callback option\n");
+#endif
+        retval = -1;            /* this ends option processing */
+      }
+      break;
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+  }
+
+  /* free memory, assign results and return */
+
+  SC_FREE (longopts);
+  sc_options_free_args (opt);
+
+  opt->first_arg = (retval < 0 ? -1 : optind);
+  opt->argc = argc;
+  opt->argv = argv;
+
+  return opt->first_arg;
+}
+
+int
+sc_options_load_args (int package_id, int err_priority, sc_options_t * opt,
+                      const char *inifile)
+{
+  int                 i, count;
+  int                 iserror;
+  dictionary         *dict;
+  const char         *s;
+  char                key[BUFSIZ];
+
+  dict = iniparser_load (inifile);
+  if (dict == NULL) {
+    SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                "Could not load or parse inifile\n");
+    return -1;
+  }
+
+  count = sc_iniparser_getint (dict, "Arguments:count", -1, &iserror);
+  if (count < 0 || iserror) {
+    SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                "Invalid or missing argument count\n");
+    iniparser_freedict (dict);
+    return -1;
+  }
+
+  sc_options_free_args (opt);
+  opt->args_alloced = 1;
+  opt->first_arg = 0;
+  opt->argc = count;
+  opt->argv = SC_ALLOC (char *, count);
+  memset (opt->argv, 0, count * sizeof (char *));
+
+  for (i = 0; i < count; ++i) {
+    snprintf (key, BUFSIZ, "Arguments:%d", i);
+    s = iniparser_getstring (dict, key, NULL);
+    if (s == NULL) {
+      SC_GEN_LOG (package_id, SC_LC_GLOBAL, err_priority,
+                  "Invalid or missing argument count\n");
+      iniparser_freedict (dict);
+      return -1;
+    }
+    opt->argv[i] = SC_STRDUP (s);
+  }
+
+  iniparser_freedict (dict);
+  return 0;
+}
diff --git a/sc/src/sc_options.h b/sc/src/sc_options.h
new file mode 100644
index 0000000..c107cde
--- /dev/null
+++ b/sc/src/sc_options.h
@@ -0,0 +1,292 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_OPTIONS_H
+#define SC_OPTIONS_H
+
+#include <sc_containers.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef enum
+{
+  SC_OPTION_SWITCH,
+  SC_OPTION_BOOL,
+  SC_OPTION_INT,
+  SC_OPTION_SIZE_T,
+  SC_OPTION_DOUBLE,
+  SC_OPTION_STRING,
+  SC_OPTION_INIFILE,
+  SC_OPTION_CALLBACK
+}
+sc_option_type_t;
+
+typedef struct
+{
+  sc_option_type_t    opt_type;
+  int                 opt_char;
+  const char         *opt_name;
+  void               *opt_var;
+  void                (*opt_fn) (void);
+  int                 has_arg;
+  int                 called;
+  const char         *help_string;
+  char               *string_value;
+  void               *user_data;
+}
+sc_option_item_t;
+
+typedef struct
+{
+  char                program_path[BUFSIZ];
+  const char         *program_name;
+  sc_array_t         *option_items;
+  int                 args_alloced;
+  int                 first_arg;
+  int                 argc;
+  char              **argv;
+  sc_array_t         *subopt_names;
+}
+sc_options_t;
+
+/** This callback can be invoked during sc_options_parse.
+ * \param [in] optarg   The option argument or NULL if there is none.
+ * \param [in] data     User-defined data passed to sc_options_add_callback.
+ * \return              Return 0 if successful, -1 on error.
+ */
+typedef int         (*sc_options_callback_t) (sc_options_t * opt,
+                                              const char *optarg, void *data);
+
+/**
+ * Create an empty options structure.
+ * \param [in] program_path   Name or path name of the program.
+ */
+sc_options_t       *sc_options_new (const char *program_path);
+
+/**
+ * Destroy the options structure.
+ */
+void                sc_options_destroy (sc_options_t * opt);
+
+/**
+ * Add a switch option. This option is used without option arguments.
+ * Every use increments the variable by one.  Its initial value is 0.
+ * Either opt_char or opt_name must be valid, that is, not '\0'/NULL.
+ * \param [in] opt_char      Short option character, may be '\0'.
+ * \param [in] opt_name      Option name without initial dashes, may be NULL.
+ * \param [in] variable      Address of the variable to store the option value.
+ * \param [in] help_string   Help string for usage message, may be NULL.
+ */
+void                sc_options_add_switch (sc_options_t * opt,
+                                           int opt_char,
+                                           const char *opt_name,
+                                           int *variable,
+                                           const char *help_string);
+
+/**
+ * Add a boolean option. It can be initialized to true or false in the C sense.
+ * A use without argument sets it to true.  The argument 0/f/F/n/N sets it
+ * to false (0).  The argument 1/t/T/y/Y sets it to true (not 0).
+ */
+void                sc_options_add_bool (sc_options_t * opt,
+                                         int opt_char,
+                                         const char *opt_name,
+                                         int *variable, int init_value,
+                                         const char *help_string);
+
+/**
+ * Add an option that takes an integer argument.
+ * \param [in] init_value   The initial value of the variable.
+ */
+void                sc_options_add_int (sc_options_t * opt,
+                                        int opt_char,
+                                        const char *opt_name,
+                                        int *variable, int init_value,
+                                        const char *help_string);
+
+/**
+ * Add an option that takes a size_t argument.
+ * The value of the size_t must not be greater than LLONG_MAX.
+ * \param [in] init_value   The initial value of the variable.
+ */
+void                sc_options_add_size_t (sc_options_t * opt,
+                                           int opt_char,
+                                           const char *opt_name,
+                                           size_t * variable,
+                                           size_t init_value,
+                                           const char *help_string);
+
+/**
+ * Add an option that takes a double argument.
+ * The double must be in the legal range.  "inf" and "nan" are legal too.
+ */
+void                sc_options_add_double (sc_options_t * opt,
+                                           int opt_char,
+                                           const char *opt_name,
+                                           double *variable,
+                                           double init_value,
+                                           const char *help_string);
+
+/**
+ * Add a string option.
+ * \param [in] init_value  The default value of the option may be NULL.
+ *                         If not NULL, the value is copied internally.
+ * \param [out] variable   Will point to an internal string value.
+ */
+void                sc_options_add_string (sc_options_t * opt,
+                                           int opt_char,
+                                           const char *opt_name,
+                                           const char **variable,
+                                           const char *init_value,
+                                           const char *help_string);
+
+/**
+ * Add an option to read in a file in .ini format.
+ */
+void                sc_options_add_inifile (sc_options_t * opt,
+                                            int opt_char,
+                                            const char *opt_name,
+                                            const char *help_string);
+
+/**
+ * Add an option that calls a user-defined function.
+ * The callback function should be implemented to allow multiple calls
+ * where the last call determines the effect independent of previous ones.
+ * \param [in] has_arg  Specify if the option needs an option argument.
+ * \param [in] fn       Function to call when this option is encountered.
+ * \param [in] data     User-defined data passed to the callback.
+ */
+void                sc_options_add_callback (sc_options_t * opt,
+                                             int opt_char,
+                                             const char *opt_name,
+                                             int has_arg,
+                                             sc_options_callback_t fn,
+                                             void *data,
+                                             const char *help_string);
+
+/**
+ * Copy one set of options to another as a subset, with a prefix.
+ * \param [in,out] opt  A set of options.
+ * \param [in]  subopt  Another set of options to be copied.
+ * \param [in]  prefix  The prefix to add to option names as they are copied.
+ *                      If an option has a long name "name" in subopt, its
+ *                      name in opt is "prefix:name"; if an option only has a
+ *                      character 'c' in subopt, its name in opt is
+ *                      "prefix:-c".
+ */
+void                sc_options_add_suboptions (sc_options_t * opt,
+                                               sc_options_t * subopt,
+                                               const char *prefix);
+
+/**
+ * Print a usage message.
+ * This function uses the SC_LC_GLOBAL log category.
+ * That means the default action is to print only on rank 0.
+ * Applications can change that by providing a user-defined log handler.
+ * \param [in] package_id       Registered package id or -1.
+ * \param [in] log_priority     Log priority for output according to sc.h.
+ * \param [in] opt              The option structure.
+ * \param [in] arg_usage        If not NULL, an <ARGUMENTS> string is appended
+ *                              to the usage line.  If the string is non-empty,
+ *                              it will be printed after the option summary
+ *                              and an "ARGUMENTS:\n" title line.  Line breaks
+ *                              are identified by strtok(3) and honored.
+ */
+void                sc_options_print_usage (int package_id, int log_priority,
+                                            sc_options_t * opt,
+                                            const char *arg_usage);
+
+/**
+ * Print a summary of all option values.
+ * Prints the title "Options:" and a line for every option,
+ * then the title "Arguments:" and a line for every argument.
+ * This function uses the SC_LC_GLOBAL log category.
+ * That means the default action is to print only on rank 0.
+ * Applications can change that by providing a user-defined log handler.
+ * \param [in] package_id       Registered package id or -1.
+ * \param [in] log_priority     Log priority for output according to sc.h.
+ * \param [in] opt              The option structure.
+ */
+void                sc_options_print_summary (int package_id,
+                                              int log_priority,
+                                              sc_options_t * opt);
+
+/**
+ * Load a file in .ini format and updates entries found under [Options].  An
+ * option whose name contains a colon such as "prefix:basename" will be
+ * updated by a "basename =" entry in a [prefix] section.
+ * \param [in] package_id       Registered package id or -1.
+ * \param [in] err_priority     Error log priority according to sc.h.
+ * \param [in] opt              The option structure.
+ * \param [in] inifile          Filename of the ini file to load.
+ * \return                      Returns 0 on success, -1 on failure.
+ */
+int                 sc_options_load (int package_id, int err_priority,
+                                     sc_options_t * opt, const char *inifile);
+
+/**
+ * Save all options and arguments to a file in .ini format.
+ * This function must only be called after successful option parsing.
+ * This function should only be called on rank 0.
+ * This function will log errors with category SC_LC_GLOBAL.
+ * An options whose name contains a colon such as "prefix:basename" will be
+ * written in a section titled [prefix] as "basename =".
+ * \param [in] package_id       Registered package id or -1.
+ * \param [in] err_priority     Error log priority according to sc.h.
+ * \param [in] opt              The option structure.
+ * \param [in] filename         Filename of the ini file to save.
+ * \return                      Returns 0 on success, -1 on failure.
+ */
+int                 sc_options_save (int package_id, int err_priority,
+                                     sc_options_t * opt, const char *inifile);
+
+/**
+ * Parse command line options.
+ * \param [in] package_id       Registered package id or -1.
+ * \param [in] err_priority     Error log priority according to sc.h.
+ * \param [in] opt              The option structure.
+ * \param [in] argc             Length of argument list.
+ * \param [in,out] argv         Argument list may be permuted.
+ * \return                      Returns -1 on an invalid option, otherwise
+ *                              the position of the first non-option argument.
+ */
+int                 sc_options_parse (int package_id, int err_priority,
+                                      sc_options_t * opt, int argc,
+                                      char **argv);
+
+/**
+ * Load a file in .ini format and updates entries found under [Arguments].
+ * There needs to be a key Arguments.count specifing the number.
+ * Then as many integer keys starting with 0 need to be present.
+ * \param [in] package_id       Registered package id or -1.
+ * \param [in] err_priority     Error log priority according to sc.h.
+ * \param [in] opt              The args are stored in this option structure.
+ * \param [in] inifile          Filename of the ini file to load.
+ * \return                      Returns 0 on success, -1 on failure.
+ */
+int                 sc_options_load_args (int package_id, int err_priority,
+                                          sc_options_t * opt,
+                                          const char *inifile);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_OPTIONS_H */
diff --git a/sc/src/sc_ranges.c b/sc/src/sc_ranges.c
new file mode 100644
index 0000000..916524a
--- /dev/null
+++ b/sc/src/sc_ranges.c
@@ -0,0 +1,330 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_ranges.h>
+#include <sc_statistics.h>
+
+static int
+sc_ranges_compare (const void *v1, const void *v2)
+{
+  return *(int *) v1 - *(int *) v2;
+}
+
+int
+sc_ranges_compute (int package_id, int num_procs, const int *procs,
+                   int rank, int first_peer, int last_peer,
+                   int num_ranges, int *ranges)
+{
+  int                 i, j;
+  int                 lastw, prev, nwin, length;
+  int                 shortest_range, shortest_length;
+
+  SC_ASSERT (rank >= 0 && rank < num_procs);
+
+  /* initialize ranges as empty */
+  nwin = 0;
+  for (i = 0; i < num_ranges; ++i) {
+    ranges[2 * i] = -1;
+    ranges[2 * i + 1] = -2;
+  }
+
+  /* if no peers are present there are no ranges */
+  if (first_peer > last_peer) {
+    SC_ASSERT (first_peer == num_procs && last_peer == -1);
+    return nwin;
+  }
+
+#ifdef SC_DEBUG
+  SC_ASSERT (0 <= first_peer && first_peer <= last_peer &&
+             last_peer < num_procs);
+  SC_ASSERT (first_peer != rank && last_peer != rank);
+  SC_ASSERT (procs[first_peer] && procs[last_peer]);
+  for (j = 0; j < first_peer; ++j) {
+    SC_ASSERT (j == rank || !procs[j]);
+  }
+  for (j = last_peer + 1; j < num_procs; ++j) {
+    SC_ASSERT (j == rank || !procs[j]);
+  }
+#endif
+
+  /* find a maximum of num_ranges - 1 empty ranges with (start, end) */
+  lastw = num_ranges - 1;
+  prev = -1;
+  for (j = 0; j < num_procs; ++j) {
+    if (!procs[j] || j == rank) {
+      continue;
+    }
+    if (prev == -1) {
+      prev = j;
+      continue;
+    }
+    if (prev < j - 1) {
+      length = j - 1 - prev;
+      SC_GEN_LOGF (package_id, SC_LC_NORMAL, SC_LP_DEBUG,
+                   "found empty range prev %d j %d length %d\n",
+                   prev, j, length);
+
+      /* claim unused range */
+      for (i = 0; i < num_ranges; ++i) {
+        if (ranges[2 * i] == -1) {
+          ranges[2 * i] = prev + 1;
+          ranges[2 * i + 1] = j - 1;
+          break;
+        }
+      }
+      SC_ASSERT (i < num_ranges);
+      nwin = i + 1;
+
+      /* if all ranges are used, remove the shortest */
+      if (nwin == num_ranges) {
+        nwin = lastw;
+        shortest_range = -1;
+        shortest_length = num_procs + 1;
+        for (i = 0; i < num_ranges; ++i) {
+          length = ranges[2 * i + 1] - ranges[2 * i] + 1;
+          if (length < shortest_length) {
+            shortest_range = i;
+            shortest_length = length;
+          }
+        }
+        SC_ASSERT (shortest_range >= 0 && shortest_range <= lastw);
+        if (shortest_range < lastw) {
+          ranges[2 * shortest_range] = ranges[2 * lastw];
+          ranges[2 * shortest_range + 1] = ranges[2 * lastw + 1];
+        }
+        ranges[2 * lastw] = -1;
+        ranges[2 * lastw + 1] = -2;
+      }
+    }
+    prev = j;
+  }
+  SC_ASSERT (nwin >= 0 && nwin < num_ranges);
+
+  /* sort empty ranges by start rank */
+  qsort (ranges, (size_t) nwin, 2 * sizeof (int), sc_ranges_compare);
+
+#ifdef SC_DEBUG
+  /* check consistency of empty ranges */
+  for (i = 0; i < nwin; ++i) {
+    SC_ASSERT (ranges[2 * i] <= ranges[2 * i + 1]);
+    if (i < nwin - 1) {
+      SC_ASSERT (ranges[2 * i + 1] < ranges[2 * (i + 1)] - 1);
+    }
+  }
+  for (i = nwin; i < num_ranges; ++i) {
+    SC_ASSERT (ranges[2 * i] == -1);
+    SC_ASSERT (ranges[2 * i + 1] == -2);
+  }
+  for (i = 0; i < nwin; ++i) {
+    SC_GEN_LOGF (package_id, SC_LC_NORMAL, SC_LP_DEBUG,
+                 "empty range %d from %d to %d\n",
+                 i, ranges[2 * i], ranges[2 * i + 1]);
+  }
+#endif
+
+  /* compute real ranges from empty ranges */
+  ranges[2 * nwin + 1] = last_peer;
+  for (i = nwin; i > 0; --i) {
+    ranges[2 * i] = ranges[2 * i - 1] + 1;
+    ranges[2 * i - 1] = ranges[2 * (i - 1)] - 1;
+  }
+  ranges[0] = first_peer;
+  ++nwin;
+
+#ifdef SC_DEBUG
+  for (i = 0; i < nwin; ++i) {
+    SC_ASSERT (ranges[2 * i] <= ranges[2 * i + 1]);
+    if (i < nwin - 1) {
+      SC_ASSERT (ranges[2 * i + 1] < ranges[2 * (i + 1)] - 1);
+    }
+  }
+  for (i = nwin; i < num_ranges; ++i) {
+    SC_ASSERT (ranges[2 * i] == -1);
+    SC_ASSERT (ranges[2 * i + 1] == -2);
+  }
+  for (i = 0; i < nwin - 1; ++i) {
+    for (j = ranges[2 * i + 1] + 1; j < ranges[2 * (i + 1)]; ++j) {
+      SC_ASSERT (j == rank || !procs[j]);
+    }
+  }
+  for (i = 0; i < nwin; ++i) {
+    SC_GEN_LOGF (package_id, SC_LC_NORMAL, SC_LP_DEBUG,
+                 "range %d from %d to %d\n", i,
+                 ranges[2 * i], ranges[2 * i + 1]);
+  }
+#endif
+
+  return nwin;
+}
+
+int
+sc_ranges_adaptive (int package_id, sc_MPI_Comm mpicomm,
+                    const int *procs, int *inout1, int *inout2,
+                    int num_ranges, int *ranges, int **global_ranges)
+{
+  int                 mpiret;
+  int                 j, num_procs, rank;
+  int                 first_peer, last_peer;
+  int                 local[2], global[2];
+  int                 nwin, maxwin, twomaxwin;
+
+  /* get processor related information */
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+  first_peer = *inout1;
+  last_peer = *inout2;
+
+  /* count max peers and compute the local ranges */
+  local[0] = 0;
+  for (j = 0; j < num_procs; ++j) {
+    local[0] += (procs[j] > 0 && j != rank);
+  }
+  local[1] = nwin =
+    sc_ranges_compute (package_id, num_procs, procs, rank,
+                       first_peer, last_peer, num_ranges, ranges);
+
+  /* communicate the maximum number of peers and ranges */
+  mpiret =
+    sc_MPI_Allreduce (local, global, 2, sc_MPI_INT, sc_MPI_MAX, mpicomm);
+  SC_CHECK_MPI (mpiret);
+  *inout1 = global[0];
+  *inout2 = maxwin = global[1];
+  twomaxwin = 2 * maxwin;
+  SC_ASSERT (nwin <= maxwin && maxwin <= num_ranges);
+
+  /* distribute everybody's range information */
+  if (global_ranges != NULL) {
+    *global_ranges = SC_ALLOC (int, twomaxwin * num_procs);
+    mpiret = sc_MPI_Allgather (ranges, twomaxwin, sc_MPI_INT,
+                               *global_ranges, twomaxwin, sc_MPI_INT,
+                               mpicomm);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  return nwin;
+}
+
+void
+sc_ranges_decode (int num_procs, int rank,
+                  int max_ranges, const int *global_ranges,
+                  int *num_receivers, int *receiver_ranks,
+                  int *num_senders, int *sender_ranks)
+{
+  int                 i, j;
+  int                 nr, ns;
+  const int          *the_ranges;
+
+#ifdef SC_DEBUG
+  int                 done;
+
+  /* verify consistency of ranges */
+  for (j = 0; j < num_procs; ++j) {
+    the_ranges = global_ranges + 2 * max_ranges * j;
+    done = 0;
+    for (i = 0; i < max_ranges; ++i) {
+      if (the_ranges[2 * i] < 0) {
+        done = 1;
+      }
+      if (!done) {
+        SC_ASSERT (the_ranges[2 * i] <= the_ranges[2 * i + 1]);
+        SC_ASSERT (i == 0 ||
+                   the_ranges[2 * (i - 1) + 1] + 1 < the_ranges[2 * i]);
+      }
+      else {
+        SC_ASSERT (the_ranges[2 * i] == -1 && the_ranges[2 * i + 1] == -2);
+      }
+    }
+  }
+#endif
+
+  /* identify receivers */
+  nr = 0;
+  the_ranges = global_ranges + 2 * max_ranges * rank;
+  for (i = 0; i < max_ranges; ++i) {
+    if (the_ranges[2 * i] < 0) {
+      /* this processor uses less ranges than the maximum */
+      break;
+    }
+    for (j = the_ranges[2 * i]; j <= the_ranges[2 * i + 1]; ++j) {
+      SC_ASSERT (0 <= j && j < num_procs);
+
+      /* exclude self */
+      if (j == rank) {
+        continue;
+      }
+      receiver_ranks[nr++] = j;
+    }
+  }
+  *num_receivers = nr;
+
+  /* identify senders */
+  ns = 0;
+  for (j = 0; j < num_procs; ++j) {
+    /* exclude self */
+    if (j == rank) {
+      continue;
+    }
+
+    /* look through that processor's ranges */
+    the_ranges = global_ranges + 2 * max_ranges * j;
+    for (i = 0; i < max_ranges; ++i) {
+      if (the_ranges[2 * i] < 0) {
+        /* processor j uses less ranges than the maximum */
+        break;
+      }
+      if (rank <= the_ranges[2 * i + 1]) {
+        if (rank >= the_ranges[2 * i]) {
+          /* processor j is a potential sender to rank */
+          sender_ranks[ns++] = j;
+        }
+        break;
+      }
+    }
+  }
+  *num_senders = ns;
+}
+
+void
+sc_ranges_statistics (int package_id, int log_priority,
+                      sc_MPI_Comm mpicomm, int num_procs, const int *procs,
+                      int rank, int num_ranges, int *ranges)
+{
+  int                 i, j;
+  int                 empties;
+  sc_statinfo_t       si;
+
+  empties = 0;
+  for (i = 0; i < num_ranges; ++i) {
+    SC_ASSERT (ranges[2 * i] >= 0 || ranges[2 * i] > ranges[2 * i + 1]);
+    for (j = ranges[2 * i]; j <= ranges[2 * i + 1]; ++j) {
+      empties += (j != rank && procs[j] == 0);
+    }
+  }
+
+  sc_stats_set1 (&si, (double) empties, NULL);
+  sc_stats_compute (mpicomm, 1, &si);
+  SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+               "Ranges %d nonpeer %g +- %g min/max %g %g\n",
+               num_ranges, si.average, si.standev, si.min, si.max);
+}
diff --git a/sc/src/sc_ranges.h b/sc/src/sc_ranges.h
new file mode 100644
index 0000000..193cf1d
--- /dev/null
+++ b/sc/src/sc_ranges.h
@@ -0,0 +1,114 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_RANGES_H
+#define SC_RANGES_H
+
+#include <sc.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Compute the optimal ranges of processors to talk to.
+ *
+ * \param [in] package_id   Registered package id or -1.
+ * \param [in] num_procs    Number of processors processed.
+ * \param [in] procs        Array [num_procs] interpreted as booleans.
+ *                          Nonzero entries need to be talked to.
+ * \param [in] rank         The id of the calling process.
+ *                          Will be excluded from the ranges.
+ * \param [in] first_peer   First processor to be considered.
+ * \param [in] last_peer    Last processor to be considered (inclusive).
+ * \param [in] num_ranges   The maximum number of ranges to fill.
+ * \param [in,out] ranges   Array [2 * num_ranges] that will be filled
+ *                          with beginning and ending procs (inclusive)
+ *                          that represent each range.  Values of -1
+ *                          indicate that the range is not needed.
+ * \return                  Returns the number of filled ranges.
+ */
+int                 sc_ranges_compute (int package_id, int num_procs,
+                                       const int *procs, int rank,
+                                       int first_peer, int last_peer,
+                                       int num_ranges, int *ranges);
+
+/** Compute the globally optimal ranges of processors.
+ *
+ * \param [in] package_id   Registered package id or -1.
+ * \param [in] mpicomm      MPI Communicator for Allreduce and Allgather.
+ * \param [in] procs        Same as in sc_ranges_compute ().
+ * \param [in,out] inout1
+ *     On input, first_peer as in sc_ranges_compute ().
+ *     On output, global maximum of peer counts.
+ * \param [in,out] inout2
+ *     On input, last_peer as in sc_ranges_compute ().
+ *     On output, global maximum number of ranges.
+ * \param [in] num_ranges   The maximum number of ranges to fill.
+ * \param [in,out] ranges   Array [2 * num_ranges] that will be filled
+ *                          with beginning and ending procs (inclusive)
+ *                          that represent each local range.  Values of -1
+ *                          indicate that the range is not needed.
+ * \param [out] global_ranges
+ *     If not NULL, will be allocated and filled with everybody's ranges.
+ *     Size will be 2 * inout2 * num_procs.  Must be freed with SC_FREE ().
+ * \return                  Returns the number of locally filled ranges.
+ */
+int                 sc_ranges_adaptive (int package_id, sc_MPI_Comm mpicomm,
+                                        const int *procs,
+                                        int *inout1, int *inout2,
+                                        int num_ranges, int *ranges,
+                                        int **global_ranges);
+
+/** Determine an array of receivers and an array of senders from ranges.
+ * This function is intended for compatibility and debugging only.
+ * In particular, sc_ranges_adaptive may include non-receiving processors.
+ * It is generally more efficient to use sc_notify instead of sc_ranges.
+ *
+ * \param [in] num_procs    The number of parallel processors (aka mpisize).
+ * \param [in] rank         Number of this processors (aka mpirank).
+ *                          Rank is excluded from receiver and sender output.
+ * \param [in] max_ranges   Global maximum of filled range windows as
+ *                          returned in inout2 by sc_ranges_adaptive.
+ * \param [in] global_ranges    All processor ranges from sc_ranges_adaptive.
+ * \param [out] num_receivers   Number of receiver ranks.  Greater/equal to
+ *                              number of nonzero procs in sc_ranges_compute.
+ * \param [in,out] receiver_ranks   Array of at least mpisize for output.
+ * \param [out] num_senders         Number of senders to this processor.
+ * \paarm [in,out] sender_ranks     Array of at least mpisize for output.
+ */
+void                sc_ranges_decode (int num_procs, int rank,
+                                      int max_ranges,
+                                      const int *global_ranges,
+                                      int *num_receivers, int *receiver_ranks,
+                                      int *num_senders, int *sender_ranks);
+
+/** Compute global statistical information on the ranges.
+ *
+ * \param [in] package_id       Registered package id or -1.
+ * \param [in] log_priority     Priority to use for logging.
+ */
+void                sc_ranges_statistics (int package_id, int log_priority,
+                                          sc_MPI_Comm mpicomm, int num_procs,
+                                          const int *procs, int rank,
+                                          int num_ranges, int *ranges);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_RANGES_H */
diff --git a/sc/src/sc_reduce.c b/sc/src/sc_reduce.c
new file mode 100644
index 0000000..a4b1c23
--- /dev/null
+++ b/sc/src/sc_reduce.c
@@ -0,0 +1,566 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_reduce.h>
+#include <sc_search.h>
+
+static void
+sc_reduce_alltoall (sc_MPI_Comm mpicomm,
+                    void *data, int count, sc_MPI_Datatype datatype,
+                    int groupsize, int target,
+                    int maxlevel, int level, int branch,
+                    sc_reduce_t reduce_fn)
+{
+  int                 i, l;
+  int                 mpiret;
+  int                 doall, allcount;
+  int                 myrank, peer, peer2;
+  int                 shift;
+  char               *alldata;
+  size_t              datasize;
+  sc_MPI_Request     *request, *rrequest, *srequest;
+
+  doall = 0;
+  if (target == -1) {
+    doall = 1;
+    target = 0;
+  }
+
+  SC_ASSERT (0 <= target && target < groupsize);
+
+  myrank = sc_search_bias (maxlevel, level, branch, target);
+
+  SC_ASSERT (0 <= myrank && myrank < groupsize);
+  SC_ASSERT (reduce_fn != NULL);
+
+  /* *INDENT-OFF* HORRIBLE indent bug */
+  datasize = (size_t) count * sc_mpi_sizeof (datatype);
+  /* *INDENT-ON* */
+
+  if (doall || target == myrank) {
+    allcount = 1 << level;
+
+    alldata = SC_ALLOC (char, allcount * datasize);
+    request = SC_ALLOC (sc_MPI_Request, 2 * allcount);
+    rrequest = request;
+    srequest = request + allcount;
+
+    for (i = 0; i < allcount; ++i) {
+      peer = sc_search_bias (maxlevel, level, i, target);
+
+      /* communicate with existing peers */
+      if (peer == myrank) {
+        memcpy (alldata + i * datasize, data, datasize);
+        rrequest[i] = srequest[i] = sc_MPI_REQUEST_NULL;
+      }
+      else {
+        if (peer < groupsize) {
+          mpiret =
+            sc_MPI_Irecv (alldata + i * datasize, datasize, sc_MPI_BYTE, peer,
+                          SC_TAG_REDUCE, mpicomm, rrequest + i);
+          SC_CHECK_MPI (mpiret);
+          if (doall) {
+            mpiret = sc_MPI_Isend (data, datasize, sc_MPI_BYTE,
+                                   peer, SC_TAG_REDUCE, mpicomm,
+                                   srequest + i);
+            SC_CHECK_MPI (mpiret);
+          }
+          else {
+            srequest[i] = sc_MPI_REQUEST_NULL;  /* unused */
+          }
+        }
+        else {
+          /* ignore non-existing ranks greater or equal mpisize */
+          rrequest[i] = srequest[i] = sc_MPI_REQUEST_NULL;
+        }
+      }
+    }
+
+    /* complete receive operations */
+    mpiret = sc_MPI_Waitall (allcount, rrequest, sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+
+    /* process received data in the same order as sc_reduce_recursive */
+    for (shift = 0, l = level - 1; l >= 0; ++shift, --l) {
+      for (i = 0; i < 1 << l; ++i) {
+#ifdef SC_DEBUG
+        peer = sc_search_bias (maxlevel, l + 1, 2 * i, target);
+#endif
+        peer2 = sc_search_bias (maxlevel, l + 1, 2 * i + 1, target);
+        SC_ASSERT (peer < peer2);
+
+        if (peer2 < groupsize) {
+          reduce_fn (alldata + ((2 * i + 1) << shift) * datasize,
+                     alldata + ((2 * i) << shift) * datasize,
+                     count, datatype);
+        }
+      }
+    }
+    memcpy (data, alldata, datasize);
+    SC_FREE (alldata);          /* alldata is not used in send buffers */
+
+    /* wait for sends only after computation is done */
+    if (doall) {
+      mpiret = sc_MPI_Waitall (allcount, srequest, sc_MPI_STATUSES_IGNORE);
+      SC_CHECK_MPI (mpiret);
+    }
+    SC_FREE (request);
+  }
+  else {
+    mpiret = sc_MPI_Send (data, datasize, sc_MPI_BYTE,
+                          target, SC_TAG_REDUCE, mpicomm);
+    SC_CHECK_MPI (mpiret);
+  }
+}
+
+static void
+sc_reduce_recursive (sc_MPI_Comm mpicomm,
+                     void *data, int count, sc_MPI_Datatype datatype,
+                     int groupsize, int target,
+                     int maxlevel, int level, int branch,
+                     sc_reduce_t reduce_fn)
+{
+  int                 mpiret;
+  int                 orig_target, doall;
+  int                 myrank, peer, higher;
+  char               *peerdata;
+  size_t              datasize;
+  sc_MPI_Status       rstatus;
+
+  orig_target = target;
+  doall = 0;
+  if (target == -1) {
+    doall = 1;
+    target = 0;
+  }
+
+  SC_ASSERT (0 <= target && target < groupsize);
+
+  myrank = sc_search_bias (maxlevel, level, branch, target);
+
+  SC_ASSERT (0 <= myrank && myrank < groupsize);
+  SC_ASSERT (reduce_fn != NULL);
+
+  if (level == 0) {
+    /* result is in data */
+  }
+  else if (level <= SC_REDUCE_ALLTOALL_LEVEL) {
+    /* all-to-all communication */
+    sc_reduce_alltoall (mpicomm, data, count, datatype,
+                        groupsize, orig_target,
+                        maxlevel, level, branch, reduce_fn);
+  }
+  else {
+    /* *INDENT-OFF* HORRIBLE indent bug */
+    datasize = (size_t) count * sc_mpi_sizeof (datatype);
+    /* *INDENT-ON* */
+    peer = sc_search_bias (maxlevel, level, branch ^ 0x01, target);
+    SC_ASSERT (peer != myrank);
+
+    higher = sc_search_bias (maxlevel, level - 1, branch / 2, target);
+    if (myrank == higher) {
+      if (peer < groupsize) {
+        /* temporary data to compare against peer */
+        peerdata = SC_ALLOC (char, datasize);
+
+        mpiret = sc_MPI_Recv (peerdata, datasize, sc_MPI_BYTE,
+                              peer, SC_TAG_REDUCE, mpicomm, &rstatus);
+        SC_CHECK_MPI (mpiret);
+
+        /* execute reduction operation here */
+        reduce_fn (peerdata, data, count, datatype);
+        SC_FREE (peerdata);
+      }
+
+      /* execute next higher level of recursion */
+      sc_reduce_recursive (mpicomm, data, count, datatype,
+                           groupsize, orig_target,
+                           maxlevel, level - 1, branch / 2, reduce_fn);
+
+      if (doall && peer < groupsize) {
+        /* if allreduce send back result of reduction */
+        mpiret = sc_MPI_Send (data, datasize, sc_MPI_BYTE,
+                              peer, SC_TAG_REDUCE, mpicomm);
+        SC_CHECK_MPI (mpiret);
+      }
+    }
+    else {
+      if (peer < groupsize) {
+        mpiret = sc_MPI_Send (data, datasize, sc_MPI_BYTE,
+                              peer, SC_TAG_REDUCE, mpicomm);
+        SC_CHECK_MPI (mpiret);
+        if (doall) {
+          /* if allreduce receive back result of reduction */
+          mpiret = sc_MPI_Recv (data, datasize, sc_MPI_BYTE,
+                                peer, SC_TAG_REDUCE, mpicomm, &rstatus);
+          SC_CHECK_MPI (mpiret);
+        }
+      }
+    }
+  }
+}
+
+static void
+sc_reduce_max (void *sendbuf, void *recvbuf,
+               int sendcount, sc_MPI_Datatype sendtype)
+{
+  int                 i;
+
+  if (sendtype == sc_MPI_CHAR || sendtype == sc_MPI_BYTE) {
+    const char         *s = (char *) sendbuf;
+    char               *r = (char *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_SHORT) {
+    const short        *s = (short *) sendbuf;
+    short              *r = (short *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_UNSIGNED_SHORT) {
+    const unsigned short *s = (unsigned short *) sendbuf;
+    unsigned short     *r = (unsigned short *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_INT) {
+    const int          *s = (int *) sendbuf;
+    int                *r = (int *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_UNSIGNED) {
+    const unsigned     *s = (unsigned *) sendbuf;
+    unsigned           *r = (unsigned *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_LONG) {
+    const long         *s = (long *) sendbuf;
+    long               *r = (long *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_UNSIGNED_LONG) {
+    const unsigned long *s = (unsigned long *) sendbuf;
+    unsigned long      *r = (unsigned long *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_LONG_LONG_INT) {
+    const long long    *s = (long long *) sendbuf;
+    long long          *r = (long long *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_FLOAT) {
+    const float        *s = (float *) sendbuf;
+    float              *r = (float *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_DOUBLE) {
+    const double       *s = (double *) sendbuf;
+    double             *r = (double *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_LONG_DOUBLE) {
+    const long double  *s = (long double *) sendbuf;
+    long double        *r = (long double *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] > r[i])
+        r[i] = s[i];
+  }
+  else {
+    SC_ABORT ("Unsupported MPI datatype in sc_reduce_max");
+  }
+}
+
+static void
+sc_reduce_min (void *sendbuf, void *recvbuf,
+               int sendcount, sc_MPI_Datatype sendtype)
+{
+  int                 i;
+
+  if (sendtype == sc_MPI_CHAR || sendtype == sc_MPI_BYTE) {
+    const char         *s = (char *) sendbuf;
+    char               *r = (char *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_SHORT) {
+    const short        *s = (short *) sendbuf;
+    short              *r = (short *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_UNSIGNED_SHORT) {
+    const unsigned short *s = (unsigned short *) sendbuf;
+    unsigned short     *r = (unsigned short *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_INT) {
+    const int          *s = (int *) sendbuf;
+    int                *r = (int *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_UNSIGNED) {
+    const unsigned     *s = (unsigned *) sendbuf;
+    unsigned           *r = (unsigned *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_LONG) {
+    const long         *s = (long *) sendbuf;
+    long               *r = (long *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_UNSIGNED_LONG) {
+    const unsigned long *s = (unsigned long *) sendbuf;
+    unsigned long      *r = (unsigned long *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_LONG_LONG_INT) {
+    const long long    *s = (long long *) sendbuf;
+    long long          *r = (long long *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_FLOAT) {
+    const float        *s = (float *) sendbuf;
+    float              *r = (float *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_DOUBLE) {
+    const double       *s = (double *) sendbuf;
+    double             *r = (double *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else if (sendtype == sc_MPI_LONG_DOUBLE) {
+    const long double  *s = (long double *) sendbuf;
+    long double        *r = (long double *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      if (s[i] < r[i])
+        r[i] = s[i];
+  }
+  else {
+    SC_ABORT ("Unsupported MPI datatype in sc_reduce_min");
+  }
+}
+
+static void
+sc_reduce_sum (void *sendbuf, void *recvbuf,
+               int sendcount, sc_MPI_Datatype sendtype)
+{
+  int                 i;
+
+  if (sendtype == sc_MPI_CHAR || sendtype == sc_MPI_BYTE) {
+    const char         *s = (char *) sendbuf;
+    char               *r = (char *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else if (sendtype == sc_MPI_SHORT) {
+    const short        *s = (short *) sendbuf;
+    short              *r = (short *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else if (sendtype == sc_MPI_UNSIGNED_SHORT) {
+    const unsigned short *s = (unsigned short *) sendbuf;
+    unsigned short     *r = (unsigned short *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else if (sendtype == sc_MPI_INT) {
+    const int          *s = (int *) sendbuf;
+    int                *r = (int *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else if (sendtype == sc_MPI_UNSIGNED) {
+    const unsigned     *s = (unsigned *) sendbuf;
+    unsigned           *r = (unsigned *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else if (sendtype == sc_MPI_LONG) {
+    const long         *s = (long *) sendbuf;
+    long               *r = (long *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else if (sendtype == sc_MPI_UNSIGNED_LONG) {
+    const unsigned long *s = (unsigned long *) sendbuf;
+    unsigned long      *r = (unsigned long *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else if (sendtype == sc_MPI_LONG_LONG_INT) {
+    const long long    *s = (long long *) sendbuf;
+    long long          *r = (long long *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else if (sendtype == sc_MPI_FLOAT) {
+    const float        *s = (float *) sendbuf;
+    float              *r = (float *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else if (sendtype == sc_MPI_DOUBLE) {
+    const double       *s = (double *) sendbuf;
+    double             *r = (double *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else if (sendtype == sc_MPI_LONG_DOUBLE) {
+    const long double  *s = (long double *) sendbuf;
+    long double        *r = (long double *) recvbuf;
+    for (i = 0; i < sendcount; ++i)
+      r[i] += s[i];
+  }
+  else {
+    SC_ABORT ("Unsupported MPI datatype in sc_reduce_sum");
+  }
+}
+
+static int
+sc_reduce_custom_dispatch (void *sendbuf, void *recvbuf, int sendcount,
+                           sc_MPI_Datatype sendtype, sc_reduce_t reduce_fn,
+                           int target, sc_MPI_Comm mpicomm)
+{
+  int                 mpiret;
+  int                 mpisize;
+  int                 mpirank;
+  int                 maxlevel;
+  size_t              datasize;
+
+  SC_ASSERT (sendcount >= 0);
+
+  /* *INDENT-OFF* HORRIBLE indent bug */
+  datasize = (size_t) sendcount * sc_mpi_sizeof (sendtype);
+  /* *INDENT-ON* */
+  memcpy (recvbuf, sendbuf, datasize);
+
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  SC_ASSERT (-1 <= target && target < mpisize);
+
+  maxlevel = SC_LOG2_32 (mpisize - 1) + 1;
+  sc_reduce_recursive (mpicomm, recvbuf, sendcount, sendtype, mpisize,
+                       target, maxlevel, maxlevel, mpirank, reduce_fn);
+
+  return sc_MPI_SUCCESS;
+}
+
+int
+sc_allreduce_custom (void *sendbuf, void *recvbuf, int sendcount,
+                     sc_MPI_Datatype sendtype, sc_reduce_t reduce_fn,
+                     sc_MPI_Comm mpicomm)
+{
+  return sc_reduce_custom_dispatch (sendbuf, recvbuf, sendcount,
+                                    sendtype, reduce_fn, -1, mpicomm);
+}
+
+int
+sc_reduce_custom (void *sendbuf, void *recvbuf, int sendcount,
+                  sc_MPI_Datatype sendtype, sc_reduce_t reduce_fn,
+                  int target, sc_MPI_Comm mpicomm)
+{
+  SC_CHECK_ABORT (target >= 0,
+                  "sc_reduce_custom requires non-negative target");
+
+  return sc_reduce_custom_dispatch (sendbuf, recvbuf, sendcount,
+                                    sendtype, reduce_fn, target, mpicomm);
+}
+
+static int
+sc_reduce_dispatch (void *sendbuf, void *recvbuf, int sendcount,
+                    sc_MPI_Datatype sendtype, sc_MPI_Op operation,
+                    int target, sc_MPI_Comm mpicomm)
+{
+  sc_reduce_t         reduce_fn;
+
+  if (operation == sc_MPI_MAX)
+    reduce_fn = sc_reduce_max;
+  else if (operation == sc_MPI_MIN)
+    reduce_fn = sc_reduce_min;
+  else if (operation == sc_MPI_SUM)
+    reduce_fn = sc_reduce_sum;
+  else
+    SC_ABORT ("Unsupported operation in sc_allreduce or sc_reduce");
+
+  return sc_reduce_custom_dispatch (sendbuf, recvbuf, sendcount,
+                                    sendtype, reduce_fn, target, mpicomm);
+}
+
+int
+sc_allreduce (void *sendbuf, void *recvbuf, int sendcount,
+              sc_MPI_Datatype sendtype, sc_MPI_Op operation,
+              sc_MPI_Comm mpicomm)
+{
+  return sc_reduce_dispatch (sendbuf, recvbuf, sendcount,
+                             sendtype, operation, -1, mpicomm);
+}
+
+int
+sc_reduce (void *sendbuf, void *recvbuf, int sendcount,
+           sc_MPI_Datatype sendtype, sc_MPI_Op operation,
+           int target, sc_MPI_Comm mpicomm)
+{
+  SC_CHECK_ABORT (target >= 0, "sc_reduce requires non-negative target");
+
+  return sc_reduce_dispatch (sendbuf, recvbuf, sendcount,
+                             sendtype, operation, target, mpicomm);
+}
diff --git a/sc/src/sc_reduce.h b/sc/src/sc_reduce.h
new file mode 100644
index 0000000..d1363f1
--- /dev/null
+++ b/sc/src/sc_reduce.h
@@ -0,0 +1,69 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_REDUCE_H
+#define SC_REDUCE_H
+
+#include <sc.h>
+
+/* highest level that uses all-to-all instead of recursion */
+#ifndef SC_REDUCE_ALLTOALL_LEVEL
+#define SC_REDUCE_ALLTOALL_LEVEL        3
+#endif
+
+SC_EXTERN_C_BEGIN;
+
+typedef void        (*sc_reduce_t) (void *sendbuf, void *recvbuf,
+                                    int sendcount, sc_MPI_Datatype sendtype);
+
+/** Custom allreduce operation.
+ */
+int                 sc_allreduce_custom (void *sendbuf, void *recvbuf,
+                                         int sendcount,
+                                         sc_MPI_Datatype sendtype,
+                                         sc_reduce_t reduce_fn,
+                                         sc_MPI_Comm mpicomm);
+
+/** Custom reduce operation.
+ * \param [in] target   The MPI rank that obtains the result.
+ */
+int                 sc_reduce_custom (void *sendbuf, void *recvbuf,
+                                      int sendcount, sc_MPI_Datatype sendtype,
+                                      sc_reduce_t reduce_fn,
+                                      int target, sc_MPI_Comm mpicomm);
+
+/** Drop-in MPI_Allreduce replacement.
+ */
+int                 sc_allreduce (void *sendbuf, void *recvbuf, int sendcount,
+                                  sc_MPI_Datatype sendtype,
+                                  sc_MPI_Op operation, sc_MPI_Comm mpicomm);
+
+/** Drop-in MPI_Reduce replacement.
+ * \param [in] target   The MPI rank that obtains the result.
+ */
+int                 sc_reduce (void *sendbuf, void *recvbuf, int sendcount,
+                               sc_MPI_Datatype sendtype, sc_MPI_Op operation,
+                               int target, sc_MPI_Comm mpicomm);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_REDUCE_H */
diff --git a/sc/src/sc_search.c b/sc/src/sc_search.c
new file mode 100644
index 0000000..2197286
--- /dev/null
+++ b/sc/src/sc_search.c
@@ -0,0 +1,142 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_search.h>
+
+int
+sc_search_bias (int maxlevel, int level, int interval, int target)
+{
+  const int           left = interval << (maxlevel - level);
+  const int           right = left + (1 << (maxlevel - level));
+  int                 result;
+
+  SC_ASSERT (0 <= level && level <= maxlevel);
+  SC_ASSERT (0 <= interval && interval < 1 << level);
+  SC_ASSERT (0 <= left && left < right && right <= 1 << maxlevel);
+
+  result = target < left ? left : target >= right ? right - 1 :
+    left + (target & ((1 << (maxlevel - level)) - 1));
+
+  SC_ASSERT (left <= result && result < right);
+  SC_ASSERT (0 <= result && result < 1 << maxlevel);
+
+  return result;
+}
+
+ssize_t
+sc_search_lower_bound64 (int64_t target, const int64_t * array,
+                         size_t nmemb, size_t guess)
+{
+  size_t              k_low, k_high;
+  int64_t             cur;
+
+  if (nmemb == 0) {
+    return -1;
+  }
+
+  k_low = 0;
+  k_high = nmemb - 1;
+  for (;;) {
+    SC_ASSERT (k_low <= k_high);
+    SC_ASSERT (k_low < nmemb && k_high < nmemb);
+    SC_ASSERT (k_low <= guess && guess <= k_high);
+
+    /* compare two quadrants */
+    cur = array[guess];
+
+    /* check if guess is higher or equal target and there's room below it */
+    if (target <= cur && (guess > 0 && target <= array[guess - 1])) {
+      k_high = guess - 1;
+      guess = (k_low + k_high + 1) / 2;
+      continue;
+    }
+
+    /* check if guess is lower than target */
+    if (target > cur) {
+      k_low = guess + 1;
+      if (k_low > k_high) {
+        return -1;
+      }
+      guess = (k_low + k_high) / 2;
+      continue;
+    }
+
+    /* otherwise guess is the correct position */
+    break;
+  }
+
+  SC_ASSERT (0 <= guess && guess < nmemb);
+  SC_ASSERT (array[guess] >= target);
+  SC_ASSERT (guess == 0 || array[guess - 1] < target);
+  return (ssize_t) guess;
+}
+
+size_t
+sc_bsearch_range (const void *key, const void *base, size_t nmemb,
+                  size_t size, int (*compar) (const void *, const void *))
+{
+  const char         *ckey = (char *) key;
+  const char         *cbase = (char *) base;
+  size_t              k_low, k_high, guess;
+
+  if (nmemb == 0) {
+    /* we need at least two array elements, nmemb >= 1, to search */
+    return nmemb;
+  }
+
+  k_low = 0;
+  k_high = nmemb - 1;
+  guess = nmemb / 2;
+  for (;;) {
+    SC_ASSERT (k_low <= k_high);
+    SC_ASSERT (k_low <= guess && guess <= k_high);
+    SC_ASSERT (k_low < nmemb && k_high < nmemb);
+
+    /* check if we have to search lower */
+    if (compar (ckey, cbase + guess * size) < 0) {
+      if (guess == k_low) {
+        return nmemb;
+      }
+      k_high = guess - 1;
+      guess = (k_low + k_high + 1) / 2;
+      continue;
+    }
+
+    /* check if we have to search higher */
+    if (compar (cbase + (guess + 1) * size, ckey) <= 0) {
+      if (guess == k_high) {
+        return nmemb;
+      }
+      k_low = guess + 1;
+      guess = (k_low + k_high) / 2;
+      continue;
+    }
+
+    /* otherwise guess is the correct position */
+    break;
+  }
+
+  SC_ASSERT (guess < nmemb);
+  SC_ASSERT (compar (cbase + guess * size, ckey) <= 0);
+  SC_ASSERT (compar (ckey, cbase + (guess + 1) * size) < 0);
+  return guess;
+}
diff --git a/sc/src/sc_search.h b/sc/src/sc_search.h
new file mode 100644
index 0000000..b0a5834
--- /dev/null
+++ b/sc/src/sc_search.h
@@ -0,0 +1,71 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_SEARCH_H
+#define SC_SEARCH_H
+
+#include <sc.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Find the branch of a tree that is biased towards a target.
+ * We assume a binary tree of depth maxlevel and 0 <= target < 2**maxlevel.
+ * We search the branch towards the target on 0 <= level <= maxlevel.
+ * The branch number on level is specified by 0 <= interval < 2**level.
+ *
+ * \return          Branch position with 0 <= position <= 2**maxlevel.
+ */
+int                 sc_search_bias (int maxlevel, int level,
+                                    int interval, int target);
+
+/** Find lowest position k in a sorted array such that array[k] >= target.
+ * \param [in]  target  The target lower bound to binary search for.
+ * \param [in]  array   The 64bit integer array to binary search in.
+ * \param [in]  nmemb   The number of int64_t's in the array.
+ * \param [in]  guess   Initial array position to look at.
+ * \return  Returns the matching position
+ *          or -1 if array[size-1] < target or if size == 0.
+ */
+ssize_t             sc_search_lower_bound64 (int64_t target,
+                                             const int64_t * array,
+                                             size_t nmemb, size_t guess);
+
+/** Search position k in sorted array with array[k] <= target < array[k + 1].
+ * This function is modeled after the libc bsearch function.
+ * \param [in]  key     The target to find in the array range.
+ * \param [in]  base    The array to binary search in.
+ * \param [in]  nmemb   Number of entries in the array MINUS ONE.
+ *                      Thus the array always contains at least one element.
+ * \param [in]  size    Size of one entry in the array in bytes.
+ * \param [in]  compar  Comparison function to return < 0 for less than,
+ *                      0 for equal, and > 0 for greater between the arguments.
+ * \return              The matching array position if found, or nmemb if not,
+ *                      i.e., if target < array[0] or target >= array[nmemb].
+ */
+size_t              sc_bsearch_range (const void *key, const void *base,
+                                      size_t nmemb, size_t size,
+                                      int (*compar) (const void *,
+                                                     const void *));
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_SEARCH_H */
diff --git a/sc/src/sc_sort.c b/sc/src/sc_sort.c
new file mode 100644
index 0000000..1f13d32
--- /dev/null
+++ b/sc/src/sc_sort.c
@@ -0,0 +1,450 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_containers.h>
+#include <sc_sort.h>
+
+typedef struct sc_psort_peer
+{
+  int                 received;
+  int                 sent;
+  int                 prank;
+  size_t              length;
+  char               *buffer;
+  char               *my_start;
+}
+sc_psort_peer_t;
+
+typedef struct sc_psort
+{
+  sc_MPI_Comm         mpicomm;
+  int                 num_procs, rank;
+  size_t              size;
+  size_t              my_lo, my_hi, my_count;
+  size_t             *gmemb;
+  char               *my_base;
+}
+sc_psort_t;
+
+/* qsort is not reentrant, so we do the inverse static */
+static int          (*sc_compare) (const void *, const void *);
+static int
+sc_icompare (const void *v1, const void *v2)
+{
+  return sc_compare (v2, v1);
+}
+
+static              size_t
+sc_bsearch_cumulative (const size_t * cumulative, size_t nmemb,
+                       size_t pos, size_t guess)
+{
+  size_t              proc_low, proc_high;
+
+  proc_low = 0;
+  proc_high = nmemb - 1;
+
+  for (;;) {
+    SC_ASSERT (proc_low <= proc_high);
+    SC_ASSERT (proc_low < nmemb && proc_high < nmemb);
+    SC_ASSERT (proc_low <= guess && guess <= proc_high);
+
+    /* check if pos is on a lower owner than guess */
+    if (pos < cumulative[guess]) {
+      proc_high = guess - 1;
+      guess = (proc_low + proc_high + 1) / 2;
+      continue;
+    }
+
+    /* check if pos is on a higher owner than guess */
+    if (cumulative[guess + 1] <= pos) {
+      proc_low = guess + 1;
+      guess = (proc_low + proc_high) / 2;
+      continue;
+    }
+
+    /* otherwise guess is the correct owner */
+    break;
+  }
+
+  /* make sure we found a valid owner with nonzero count */
+  SC_ASSERT (guess < nmemb);
+  SC_ASSERT (cumulative[guess] <= pos && pos < cumulative[guess + 1]);
+  return guess;
+}
+
+static void
+sc_merge_bitonic (sc_psort_t * pst, size_t lo, size_t hi, int dir)
+{
+  const size_t        n = hi - lo;
+
+  if (n > 1 && pst->my_hi > lo && pst->my_lo < hi) {
+    const int           rank = pst->rank;
+    const size_t        size = pst->size;
+    size_t              k, n2;
+    size_t              lo_end, hi_beg;
+    size_t              lo_length, hi_length;
+    size_t              offset, max_length;
+    int                 lo_owner, hi_owner;
+    int                 num_peers, remaining, remaining2, outcount, outcount2;
+    int                 mpiret;
+    sc_array_t          a, *pa = &a;
+    sc_array_t          r, *pr = &r;
+    sc_array_t          s, *ps = &s;
+    int                *wait_indices, *wait_indices2;
+    sc_MPI_Status      *recv_statuses, *recv_statuses2;
+    sc_psort_peer_t    *peer;
+
+    for (k = 1; k < n;) {
+      k = k << 1;
+    }
+    n2 = k >> 1;
+    SC_ASSERT (n2 >= n / 2 && n2 < n);
+
+    lo_end = lo + n - n2;
+    hi_beg = lo + n2;
+    SC_ASSERT (lo_end <= hi_beg && lo_end - lo == hi - hi_beg);
+
+    sc_array_init (pa, sizeof (sc_psort_peer_t));
+    sc_array_init (pr, sizeof (sc_MPI_Request));
+    sc_array_init (ps, sizeof (sc_MPI_Request));
+
+    /* loop 1: initiate communication */
+    lo_owner = hi_owner = rank;
+    offset = max_length = 0;
+    for (offset = 0; offset < lo_end - lo; offset += max_length) {
+      lo_owner =
+        (int) sc_bsearch_cumulative (pst->gmemb, (size_t) pst->num_procs,
+                                     lo + offset, (size_t) lo_owner);
+      lo_length = pst->gmemb[lo_owner + 1] - (lo + offset);
+      hi_owner =
+        (int) sc_bsearch_cumulative (pst->gmemb, (size_t) pst->num_procs,
+                                     hi_beg + offset, (size_t) hi_owner);
+      hi_length = pst->gmemb[hi_owner + 1] - (hi_beg + offset);
+      max_length = lo_end - (lo + offset);
+      max_length = SC_MIN (max_length, SC_MIN (lo_length, hi_length));
+      SC_ASSERT (max_length > 0);
+
+      if (lo_owner == rank && hi_owner != rank) {
+        char               *lo_data;
+        sc_MPI_Request     *rreq, *sreq;
+        const int           bytes = (int) (max_length * size);
+
+        /* receive high part, send low part */
+        peer = (sc_psort_peer_t *) sc_array_push (pa);
+        rreq = (sc_MPI_Request *) sc_array_push (pr);
+        sreq = (sc_MPI_Request *) sc_array_push (ps);
+        lo_data = pst->my_base + (lo + offset - pst->my_lo) * size;
+
+        peer->received = 0;
+        peer->sent = 0;
+        peer->prank = hi_owner;
+        peer->length = max_length;
+        peer->buffer = SC_ALLOC (char, bytes);
+        peer->my_start = lo_data;
+        mpiret = sc_MPI_Irecv (peer->buffer, bytes, sc_MPI_BYTE,
+                               peer->prank, SC_TAG_PSORT_HI, pst->mpicomm,
+                               rreq);
+        SC_CHECK_MPI (mpiret);
+
+        SC_ASSERT (lo_data >= pst->my_base);
+        SC_ASSERT (lo_data + bytes <= pst->my_base + pst->my_count * size);
+        mpiret = sc_MPI_Isend (lo_data, bytes, sc_MPI_BYTE,
+                               peer->prank, SC_TAG_PSORT_LO, pst->mpicomm,
+                               sreq);
+        SC_CHECK_MPI (mpiret);
+      }
+      else if (lo_owner != rank && hi_owner == rank) {
+        char               *hi_data;
+        sc_MPI_Request     *rreq, *sreq;
+        const int           bytes = (int) (max_length * size);
+
+        /* receive low part, send high part */
+        peer = (sc_psort_peer_t *) sc_array_push (pa);
+        rreq = (sc_MPI_Request *) sc_array_push (pr);
+        sreq = (sc_MPI_Request *) sc_array_push (ps);
+        hi_data = pst->my_base + (hi_beg + offset - pst->my_lo) * size;
+
+        peer->received = 0;
+        peer->sent = 0;
+        peer->prank = lo_owner;
+        peer->length = max_length;
+        peer->buffer = SC_ALLOC (char, bytes);
+        peer->my_start = hi_data;
+
+        mpiret = sc_MPI_Irecv (peer->buffer, bytes, sc_MPI_BYTE,
+                               peer->prank, SC_TAG_PSORT_LO, pst->mpicomm,
+                               rreq);
+        SC_CHECK_MPI (mpiret);
+
+        SC_ASSERT (hi_data >= pst->my_base);
+        SC_ASSERT (hi_data + bytes <= pst->my_base + pst->my_count * size);
+        mpiret = sc_MPI_Isend (hi_data, bytes, sc_MPI_BYTE,
+                               peer->prank, SC_TAG_PSORT_HI, pst->mpicomm,
+                               sreq);
+        SC_CHECK_MPI (mpiret);
+      }
+    }
+
+    /* loop 2: local computation */
+    lo_owner = hi_owner = rank;
+    offset = max_length = 0;
+    for (offset = 0; offset < lo_end - lo; offset += max_length) {
+      lo_owner =
+        (int) sc_bsearch_cumulative (pst->gmemb, (size_t) pst->num_procs,
+                                     lo + offset, (size_t) lo_owner);
+      lo_length = pst->gmemb[lo_owner + 1] - (lo + offset);
+      hi_owner =
+        (int) sc_bsearch_cumulative (pst->gmemb, (size_t) pst->num_procs,
+                                     hi_beg + offset, (size_t) hi_owner);
+      hi_length = pst->gmemb[hi_owner + 1] - (hi_beg + offset);
+      max_length = lo_end - (lo + offset);
+      max_length = SC_MIN (max_length, SC_MIN (lo_length, hi_length));
+      SC_ASSERT (max_length > 0);
+
+      if (lo_owner == rank && hi_owner == rank) {
+        size_t              zz;
+        char               *lo_data, *hi_data;
+        char               *temp = SC_ALLOC (char, size);
+
+        /* local comparisons only */
+        lo_data = pst->my_base + (lo + offset - pst->my_lo) * size;
+        hi_data = pst->my_base + (hi_beg + offset - pst->my_lo) * size;
+        for (zz = 0; zz < max_length; ++zz) {
+          if (dir == (sc_compare (lo_data, hi_data) > 0)) {
+            memcpy (temp, lo_data, size);
+            memcpy (lo_data, hi_data, size);
+            memcpy (hi_data, temp, size);
+          }
+          lo_data += size;
+          hi_data += size;
+        }
+        SC_FREE (temp);
+      }
+    }
+
+    /* loop 3: receive and compute with received data */
+    outcount = 0;
+    outcount2 = 0;
+    num_peers = (int) pa->elem_count;
+    wait_indices = SC_ALLOC (int, num_peers);
+    wait_indices2 = SC_ALLOC (int, num_peers);
+    recv_statuses = SC_ALLOC (sc_MPI_Status, num_peers);
+    recv_statuses2 = SC_ALLOC (sc_MPI_Status, num_peers);
+    for (remaining = num_peers, remaining2 = num_peers;
+         remaining > 0 || remaining2 > 0;
+         remaining -= outcount, remaining2 -= outcount2) {
+      int                 i;
+      outcount = 0;
+      outcount2 = 0;
+      if (remaining > 0) {
+        mpiret = sc_MPI_Waitsome (num_peers, (sc_MPI_Request *) pr->array,
+                                  &outcount, wait_indices, recv_statuses);
+        SC_CHECK_MPI (mpiret);
+        SC_ASSERT (outcount != sc_MPI_UNDEFINED);
+        SC_ASSERT (outcount > 0);
+        for (i = 0; i < outcount; ++i) {
+          size_t              zz;
+          char               *lo_data, *hi_data;
+#ifdef SC_DEBUG
+          sc_MPI_Status      *jstatus;
+
+          jstatus = &recv_statuses[i];
+#endif
+
+          /* retrieve peer information */
+          peer = (sc_psort_peer_t *) sc_array_index_int (pa, wait_indices[i]);
+          if (peer->sent) {
+            /* only overwrite data,
+             * if made sure it has already been sent to the other process */
+            SC_ASSERT (!peer->received);
+            SC_ASSERT (peer->prank != rank);
+            SC_ASSERT (peer->prank == jstatus->MPI_SOURCE);
+
+            /* comparisons with remote peer */
+            if (rank < peer->prank) {
+              lo_data = peer->my_start;
+              hi_data = peer->buffer;
+              for (zz = 0; zz < peer->length; ++zz) {
+                if (dir == (sc_compare (lo_data, hi_data) > 0)) {
+                  memcpy (lo_data, hi_data, size);
+                }
+                lo_data += size;
+                hi_data += size;
+              }
+            }
+            else {
+              lo_data = peer->buffer;
+              hi_data = peer->my_start;
+              for (zz = 0; zz < peer->length; ++zz) {
+                if (dir == (sc_compare (lo_data, hi_data) > 0)) {
+                  memcpy (hi_data, lo_data, size);
+                }
+                lo_data += size;
+                hi_data += size;
+              }
+            }
+
+            /* close down this peer */
+            SC_FREE (peer->buffer);
+          }
+          peer->received = 1;
+        }
+      }
+
+      if (remaining2 > 0) {
+        mpiret =
+          sc_MPI_Waitsome (num_peers, (sc_MPI_Request *) ps->array,
+                           &outcount2, wait_indices2, recv_statuses2);
+        SC_CHECK_MPI (mpiret);
+        SC_ASSERT (outcount2 != sc_MPI_UNDEFINED);
+        SC_ASSERT (outcount2 > 0);
+        for (i = 0; i < outcount2; ++i) {
+          size_t              zz;
+          char               *lo_data, *hi_data;
+
+          /* retrieve peer information */
+          peer =
+            (sc_psort_peer_t *) sc_array_index_int (pa, wait_indices2[i]);
+
+          if (peer->received) {
+            SC_ASSERT (!peer->sent);
+
+            /* comparisons with remote peer */
+            if (rank < peer->prank) {
+              lo_data = peer->my_start;
+              hi_data = peer->buffer;
+              for (zz = 0; zz < peer->length; ++zz) {
+                if (dir == (sc_compare (lo_data, hi_data) > 0)) {
+                  memcpy (lo_data, hi_data, size);
+                }
+                lo_data += size;
+                hi_data += size;
+              }
+            }
+            else {
+              lo_data = peer->buffer;
+              hi_data = peer->my_start;
+              for (zz = 0; zz < peer->length; ++zz) {
+                if (dir == (sc_compare (lo_data, hi_data) > 0)) {
+                  memcpy (hi_data, lo_data, size);
+                }
+                lo_data += size;
+                hi_data += size;
+              }
+            }
+
+            /* close down this peer */
+            SC_FREE (peer->buffer);
+          }
+          peer->sent = 1;
+        }
+      }
+    }
+    SC_ASSERT (remaining == 0);
+    SC_ASSERT (remaining2 == 0);
+    SC_FREE (recv_statuses);
+    SC_FREE (wait_indices);
+    SC_FREE (recv_statuses2);
+    SC_FREE (wait_indices2);
+
+    /* clean up */
+    if (num_peers > 0) {
+      mpiret = sc_MPI_Waitall (num_peers, (sc_MPI_Request *) ps->array,
+                               sc_MPI_STATUSES_IGNORE);
+      SC_CHECK_MPI (mpiret);
+    }
+    sc_array_reset (pa);
+    sc_array_reset (pr);
+    sc_array_reset (ps);
+
+    /* recursive merge */
+    sc_merge_bitonic (pst, lo, lo + n2, dir);
+    sc_merge_bitonic (pst, lo + n2, hi, dir);
+  }
+}
+
+static void
+sc_psort_bitonic (sc_psort_t * pst, size_t lo, size_t hi, int dir)
+{
+  const size_t        n = hi - lo;
+
+  if (n > 1 && pst->my_hi > lo && pst->my_lo < hi) {
+    if (lo >= pst->my_lo && hi <= pst->my_hi) {
+      qsort (pst->my_base + (lo - pst->my_lo) * pst->size,
+             n, pst->size, dir ? sc_compare : sc_icompare);
+    }
+    else {
+      const size_t        n2 = n / 2;
+
+      sc_psort_bitonic (pst, lo, lo + n2, !dir);
+      sc_psort_bitonic (pst, lo + n2, hi, dir);
+      sc_merge_bitonic (pst, lo, hi, dir);
+    }
+  }
+}
+
+void
+sc_psort (sc_MPI_Comm mpicomm, void *base, size_t * nmemb, size_t size,
+          int (*compar) (const void *, const void *))
+{
+  int                 mpiret;
+  int                 num_procs, rank;
+  int                 i;
+  size_t              total;
+  size_t             *gmemb;
+  sc_psort_t          pst;
+
+  SC_ASSERT (sc_compare == NULL);
+
+  /* get basic MPI information */
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  /* alloc global offset array */
+  gmemb = SC_ALLOC (size_t, num_procs + 1);
+  gmemb[0] = 0;
+  for (i = 0; i < num_procs; ++i) {
+    gmemb[i + 1] = gmemb[i] + nmemb[i];
+  }
+
+  /* set up internal state and call recursion */
+  pst.mpicomm = mpicomm;
+  pst.num_procs = num_procs;
+  pst.rank = rank;
+  pst.size = size;
+  pst.my_lo = gmemb[rank];
+  pst.my_hi = gmemb[rank + 1];
+  pst.my_count = nmemb[rank];
+  SC_ASSERT (pst.my_lo + pst.my_count == pst.my_hi);
+  pst.gmemb = gmemb;
+  pst.my_base = (char *) base;
+  sc_compare = compar;
+  total = gmemb[num_procs];
+  SC_GLOBAL_LDEBUGF ("Total values to sort %lld\n", (long long) total);
+  sc_psort_bitonic (&pst, 0, total, 1);
+
+  /* clean up and free memory */
+  sc_compare = NULL;
+  SC_FREE (gmemb);
+}
diff --git a/sc/src/sc_sort.h b/sc/src/sc_sort.h
new file mode 100644
index 0000000..dd89a85
--- /dev/null
+++ b/sc/src/sc_sort.h
@@ -0,0 +1,45 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_SORT_H
+#define SC_SORT_H
+
+#include <sc.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Sort a distributed set of values in parallel.
+ * This algorithm uses bitonic sort between processors and qsort locally.
+ * The partition of the data can be arbitrary and is not changed.
+ * \param [in] mpicomm          Communicator to use.
+ * \param [in] base             Pointer to the local subset of data.
+ * \param [in] nmemb            Array of mpisize counts of local data.
+ * \param [in] size             Size in bytes of each data value.
+ * \param [in] compar           Comparison function to use.
+ */
+void                sc_psort (sc_MPI_Comm mpicomm, void *base,
+                              size_t * nmemb, size_t size,
+                              int (*compar) (const void *, const void *));
+
+SC_EXTERN_C_END;
+
+#endif /* SC_SORT_H */
diff --git a/sc/src/sc_statistics.c b/sc/src/sc_statistics.c
new file mode 100644
index 0000000..1786db7
--- /dev/null
+++ b/sc/src/sc_statistics.c
@@ -0,0 +1,415 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_statistics.h>
+
+#ifdef SC_ENABLE_MPI
+
+static void
+sc_stats_mpifunc (void *invec, void *inoutvec, int *len,
+                  sc_MPI_Datatype * datatype)
+{
+  int                 i;
+  double             *in = (double *) invec;
+  double             *inout = (double *) inoutvec;
+
+  for (i = 0; i < *len; ++i) {
+    /* sum count, values and their squares */
+    inout[0] += in[0];
+    if (in[0]) {                /* ignore statistics when no count */
+      inout[1] += in[1];
+      inout[2] += in[2];
+
+      /* compute minimum and its rank */
+      if (in[3] < inout[3]) {
+        inout[3] = in[3];
+        inout[5] = in[5];
+      }
+      else if (in[3] == inout[3]) {     /* ignore the comparison warning */
+        inout[5] = SC_MIN (in[5], inout[5]);
+      }
+
+      /* compute maximum and its rank */
+      if (in[4] > inout[4]) {
+        inout[4] = in[4];
+        inout[6] = in[6];
+      }
+      else if (in[4] == inout[4]) {     /* ignore the comparison warning */
+        inout[6] = SC_MIN (in[6], inout[6]);
+      }
+    }
+
+    /* advance to next data set */
+    in += 7;
+    inout += 7;
+  }
+}
+
+#endif /* SC_ENABLE_MPI */
+
+void
+sc_stats_set1 (sc_statinfo_t * stats, double value, const char *variable)
+{
+  stats->dirty = 1;
+  stats->count = 1;
+  stats->sum_values = value;
+  stats->sum_squares = value * value;
+  stats->min = value;
+  stats->max = value;
+  stats->variable = variable;
+}
+
+void
+sc_stats_init (sc_statinfo_t * stats, const char *variable)
+{
+  stats->dirty = 1;
+  stats->count = 0;
+  stats->sum_values = stats->sum_squares = 0.;
+  stats->min = stats->max = 0.;
+  stats->variable = variable;
+}
+
+void
+sc_stats_accumulate (sc_statinfo_t * stats, double value)
+{
+  SC_ASSERT (stats->dirty);
+  if (stats->count) {
+    stats->count++;
+    stats->sum_values += value;
+    stats->sum_squares += value * value;
+    stats->min = SC_MIN (stats->min, value);
+    stats->max = SC_MAX (stats->max, value);
+  }
+  else {
+    stats->count = 1;
+    stats->sum_values = value;
+    stats->sum_squares = value * value;
+    stats->min = value;
+    stats->max = value;
+  }
+}
+
+void
+sc_stats_compute (sc_MPI_Comm mpicomm, int nvars, sc_statinfo_t * stats)
+{
+  int                 i;
+  int                 mpiret;
+  int                 rank;
+  double              cnt, avg;
+  double             *flat;
+  double             *flatin;
+  double             *flatout;
+#ifdef SC_ENABLE_MPI
+  sc_MPI_Op           op;
+  sc_MPI_Datatype     ctype;
+#endif
+
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  flat = SC_ALLOC (double, 2 * 7 * nvars);
+  flatin = flat;
+  flatout = flat + 7 * nvars;
+
+  for (i = 0; i < nvars; ++i) {
+    if (!stats[i].dirty) {
+      memset (flatin + 7 * i, 0, 7 * sizeof (*flatin));
+      continue;
+    }
+    flatin[7 * i + 0] = (double) stats[i].count;
+    flatin[7 * i + 1] = stats[i].sum_values;
+    flatin[7 * i + 2] = stats[i].sum_squares;
+    flatin[7 * i + 3] = stats[i].min;
+    flatin[7 * i + 4] = stats[i].max;
+    flatin[7 * i + 5] = (double) rank;  /* rank that attains minimum */
+    flatin[7 * i + 6] = (double) rank;  /* rank that attains maximum */
+  }
+
+#ifndef SC_ENABLE_MPI
+  memcpy (flatout, flatin, 7 * nvars * sizeof (*flatout));
+#else
+  mpiret = MPI_Type_contiguous (7, MPI_DOUBLE, &ctype);
+  SC_CHECK_MPI (mpiret);
+
+  mpiret = MPI_Type_commit (&ctype);
+  SC_CHECK_MPI (mpiret);
+
+  mpiret = MPI_Op_create ((MPI_User_function *) sc_stats_mpifunc, 1, &op);
+  SC_CHECK_MPI (mpiret);
+
+  mpiret = MPI_Allreduce (flatin, flatout, nvars, ctype, op, mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  mpiret = MPI_Op_free (&op);
+  SC_CHECK_MPI (mpiret);
+
+  mpiret = MPI_Type_free (&ctype);
+  SC_CHECK_MPI (mpiret);
+#endif /* SC_ENABLE_MPI */
+
+  for (i = 0; i < nvars; ++i) {
+    if (!stats[i].dirty) {
+      continue;
+    }
+    cnt = flatout[7 * i + 0];
+    stats[i].count = (long) cnt;
+    if (!cnt) {
+      continue;
+    }
+    stats[i].sum_values = flatout[7 * i + 1];
+    stats[i].sum_squares = flatout[7 * i + 2];
+    stats[i].min = flatout[7 * i + 3];
+    stats[i].max = flatout[7 * i + 4];
+    stats[i].min_at_rank = (int) flatout[7 * i + 5];
+    stats[i].max_at_rank = (int) flatout[7 * i + 6];
+    stats[i].average = avg = stats[i].sum_values / cnt;
+    stats[i].variance = stats[i].sum_squares / cnt - avg * avg;
+    stats[i].variance = SC_MAX (stats[i].variance, 0.);
+    stats[i].variance_mean = stats[i].variance / cnt;
+    stats[i].standev = sqrt (stats[i].variance);
+    stats[i].standev_mean = sqrt (stats[i].variance_mean);
+    stats[i].dirty = 0;
+  }
+
+  SC_FREE (flat);
+}
+
+void
+sc_stats_compute1 (sc_MPI_Comm mpicomm, int nvars, sc_statinfo_t * stats)
+{
+  int                 i;
+  double              value;
+
+  for (i = 0; i < nvars; ++i) {
+    value = stats[i].sum_values;
+
+    stats[i].count = 1;
+    stats[i].sum_squares = value * value;
+    stats[i].min = value;
+    stats[i].max = value;
+  }
+
+  sc_stats_compute (mpicomm, nvars, stats);
+}
+
+void
+sc_stats_print (int package_id, int log_priority,
+                int nvars, sc_statinfo_t * stats, int full, int summary)
+{
+  int                 i, count;
+  sc_statinfo_t      *si;
+  char                buffer[BUFSIZ];
+
+  if (full) {
+    for (i = 0; i < nvars; ++i) {
+      si = &stats[i];
+      if (si->variable != NULL) {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+                     "Statistics for %s\n", si->variable);
+      }
+      else {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+                     "Statistics for %d\n", i);
+      }
+      SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+                   "   Global number of values: %5ld\n", si->count);
+      if (!si->count) {
+        continue;
+      }
+      if (si->average != 0.) {  /* ignore the comparison warning */
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+                     "   Mean value (std. dev.):         %g (%.3g = %.3g%%)\n",
+                     si->average, si->standev,
+                     100. * si->standev / fabs (si->average));
+      }
+      else {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+                     "   Mean value (std. dev.):         %g (%.3g)\n",
+                     si->average, si->standev);
+      }
+      SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+                   "   Minimum attained at rank %5d: %g\n",
+                   si->min_at_rank, si->min);
+      SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+                   "   Maximum attained at rank %5d: %g\n",
+                   si->max_at_rank, si->max);
+    }
+  }
+  else {
+    for (i = 0; i < nvars; ++i) {
+      si = &stats[i];
+      if (si->variable != NULL) {
+        snprintf (buffer, BUFSIZ, "for %s:", si->variable);
+      }
+      else {
+        snprintf (buffer, BUFSIZ, "for %d:", i);
+      }
+      if (si->average != 0.) {  /* ignore the comparison warning */
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+                     "Mean (sigma) %-28s %g (%.3g = %.3g%%)\n",
+                     buffer, si->average, si->standev,
+                     100. * si->standev / fabs (si->average));
+      }
+      else {
+        SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+                     "Mean (sigma) %-28s %g (%.3g)\n", buffer,
+                     si->average, si->standev);
+      }
+    }
+  }
+
+  if (summary) {
+    count = snprintf (buffer, BUFSIZ, "Summary = ");
+    for (i = 0; i < nvars && count >= 0 && (size_t) count < BUFSIZ; ++i) {
+      si = &stats[i];
+      count += snprintf (buffer + count, BUFSIZ - count,
+                         "%s%g", i == 0 ? "[ " : " ", si->average);
+    }
+    if (count >= 0 && (size_t) count < BUFSIZ) {
+      snprintf (buffer + count, BUFSIZ - count, "%s", " ];\n");
+      SC_GEN_LOG (package_id, SC_LC_GLOBAL, log_priority, buffer);
+    }
+    else {
+      SC_GEN_LOG (package_id, SC_LC_GLOBAL, log_priority,
+                  "Summary overflow\n");
+    }
+    count = snprintf (buffer, BUFSIZ, "Maximum = ");
+    for (i = 0; i < nvars && count >= 0 && (size_t) count < BUFSIZ; ++i) {
+      si = &stats[i];
+      count += snprintf (buffer + count, BUFSIZ - count,
+                         "%s%g", i == 0 ? "[ " : " ", si->max);
+    }
+    if (count >= 0 && (size_t) count < BUFSIZ) {
+      snprintf (buffer + count, BUFSIZ - count, "%s", " ];\n");
+      SC_GEN_LOG (package_id, SC_LC_GLOBAL, log_priority, buffer);
+    }
+    else {
+      SC_GEN_LOG (package_id, SC_LC_GLOBAL, log_priority,
+                  "Maximum overflow\n");
+    }
+  }
+}
+
+sc_statistics_t    *
+sc_statistics_new (sc_MPI_Comm mpicomm)
+{
+  sc_statistics_t    *stats;
+
+  stats = SC_ALLOC (sc_statistics_t, 1);
+  stats->mpicomm = mpicomm;
+  stats->kv = sc_keyvalue_new ();
+  stats->sarray = sc_array_new (sizeof (sc_statinfo_t));
+
+  return stats;
+}
+
+void
+sc_statistics_destroy (sc_statistics_t * stats)
+{
+  sc_keyvalue_destroy (stats->kv);
+  sc_array_destroy (stats->sarray);
+
+  SC_FREE (stats);
+}
+
+void
+sc_statistics_add (sc_statistics_t * stats, const char *name)
+{
+  int                 i;
+  sc_statinfo_t      *si;
+
+  /* always check for wrong usage and output adequate error message */
+  SC_CHECK_ABORTF (!sc_keyvalue_exists (stats->kv, name),
+                   "Statistics variable \"%s\" exists already", name);
+
+  i = (int) stats->sarray->elem_count;
+  si = (sc_statinfo_t *) sc_array_push (stats->sarray);
+  sc_stats_set1 (si, 0, name);
+
+  sc_keyvalue_set_int (stats->kv, name, i);
+}
+
+void
+sc_statistics_set (sc_statistics_t * stats, const char *name, double value)
+{
+  int                 i;
+  sc_statinfo_t      *si;
+
+  i = sc_keyvalue_get_int (stats->kv, name, -1);
+
+  /* always check for wrong usage and output adequate error message */
+  SC_CHECK_ABORTF (i >= 0, "Statistics variable \"%s\" does not exist", name);
+
+  si = (sc_statinfo_t *) sc_array_index_int (stats->sarray, i);
+
+  sc_stats_set1 (si, value, name);
+}
+
+void
+sc_statistics_add_empty (sc_statistics_t * stats, const char *name)
+{
+  int                 i;
+  sc_statinfo_t      *si;
+
+  /* always check for wrong usage and output adequate error message */
+  SC_CHECK_ABORTF (!sc_keyvalue_exists (stats->kv, name),
+                   "Statistics variable \"%s\" exists already", name);
+
+  i = (int) stats->sarray->elem_count;
+  si = (sc_statinfo_t *) sc_array_push (stats->sarray);
+  sc_stats_init (si, name);
+
+  sc_keyvalue_set_int (stats->kv, name, i);
+}
+
+void
+sc_statistics_accumulate (sc_statistics_t * stats, const char *name,
+                          double value)
+{
+  int                 i;
+  sc_statinfo_t      *si;
+
+  i = sc_keyvalue_get_int (stats->kv, name, -1);
+
+  /* always check for wrong usage and output adequate error message */
+  SC_CHECK_ABORTF (i >= 0, "Statistics variable \"%s\" does not exist", name);
+
+  si = (sc_statinfo_t *) sc_array_index_int (stats->sarray, i);
+
+  sc_stats_accumulate (si, value);
+}
+
+void
+sc_statistics_compute (sc_statistics_t * stats)
+{
+  sc_stats_compute (stats->mpicomm, (int) stats->sarray->elem_count,
+                    (sc_statinfo_t *) stats->sarray->array);
+}
+
+void
+sc_statistics_print (sc_statistics_t * stats,
+                     int package_id, int log_priority, int full, int summary)
+{
+  sc_stats_print (package_id, log_priority,
+                  (int) stats->sarray->elem_count,
+                  (sc_statinfo_t *) stats->sarray->array, full, summary);
+}
diff --git a/sc/src/sc_statistics.h b/sc/src/sc_statistics.h
new file mode 100644
index 0000000..8b76e41
--- /dev/null
+++ b/sc/src/sc_statistics.h
@@ -0,0 +1,161 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_STATISTICS_H
+#define SC_STATISTICS_H
+
+#include <sc.h>
+#include <sc_keyvalue.h>
+
+SC_EXTERN_C_BEGIN;
+
+/* sc_statinfo_t stores information for one random variable */
+typedef struct sc_statinfo
+{
+  int                 dirty;    /* only update stats if this is true */
+  long                count;    /* inout, global count is 52bit accurate */
+  double              sum_values, sum_squares, min, max;        /* inout */
+  int                 min_at_rank, max_at_rank; /* out */
+  double              average, variance, standev;       /* out */
+  double              variance_mean, standev_mean;      /* out */
+  const char         *variable; /* name of the variable for output */
+}
+sc_statinfo_t;
+
+/* sc_statistics_t allows dynamically adding random variables */
+typedef struct sc_stats
+{
+  sc_MPI_Comm         mpicomm;
+  sc_keyvalue_t      *kv;
+  sc_array_t         *sarray;
+}
+sc_statistics_t;
+
+/**
+ * Populate a sc_statinfo_t structure assuming count=1 and mark it dirty.
+ */
+void                sc_stats_set1 (sc_statinfo_t * stats,
+                                   double value, const char *variable);
+
+/**
+ * Initialize a sc_statinfo_t structure assuming count=0 and mark it dirty.
+ * This is useful if \a stats will be used to accumulate instances locally
+ * before global statistics are computed.
+ */
+void                sc_stats_init (sc_statinfo_t * stats,
+                                   const char *variable);
+
+/**
+ * Add an instance of the random variable.
+ */
+void                sc_stats_accumulate (sc_statinfo_t * stats, double value);
+
+/**
+ * Compute global average and standard deviation.
+ * Only updates dirty variables. Then removes the dirty flag.
+ * \param [in]     mpicomm   MPI communicator to use.
+ * \param [in]     nvars     Number of variables to be examined.
+ * \param [in,out] stats     Set of statisitcs for each variable.
+ * On input, set the following fields for each variable separately.
+ *    count         Number of values for each process.
+ *    sum_values    Sum of values for each process.
+ *    sum_squares   Sum of squares for each process.
+ *    min, max      Minimum and maximum of values for each process.
+ *    variable      String describing the variable, or NULL.
+ * On output, the fields have the following meaning.
+ *    count                        Global number of values.
+ *    sum_values                   Global sum of values.
+ *    sum_squares                  Global sum of squares.
+ *    min, max                     Global minimum and maximum values.
+ *    min_at_rank, max_at_rank     The ranks that attain min and max.
+ *    average, variance, standev   Global statistical measures.
+ *    variance_mean, standev_mean  Statistical measures of the mean.
+ */
+void                sc_stats_compute (sc_MPI_Comm mpicomm, int nvars,
+                                      sc_statinfo_t * stats);
+
+/**
+ * Version of sc_statistics_statistics that assumes count=1.
+ * On input, the field sum_values needs to be set to the value
+ * and the field variable must contain a valid string or NULL.
+ * Only updates dirty variables. Then removes the dirty flag.
+ */
+void                sc_stats_compute1 (sc_MPI_Comm mpicomm, int nvars,
+                                       sc_statinfo_t * stats);
+
+/**
+ * Print measured statistics.
+ * This function uses the SC_LC_GLOBAL log category.
+ * That means the default action is to print only on rank 0.
+ * Applications can change that by providing a user-defined log handler.
+ * \param [in] package_id       Registered package id or -1.
+ * \param [in] log_priority     Log priority for output according to sc.h.
+ * \param [in] full             Print full information for every variable.
+ * \param [in] summary          Print summary information all on 1 line.
+ */
+void                sc_stats_print (int package_id, int log_priority,
+                                    int nvars, sc_statinfo_t * stats,
+                                    int full, int summary);
+
+/** Create a new statistics structure that can grow dynamically.
+ */
+sc_statistics_t    *sc_statistics_new (sc_MPI_Comm mpicomm);
+void                sc_statistics_destroy (sc_statistics_t * stats);
+
+/** Register a statistics variable by name and set its value to 0.
+ * This variable must not exist already.
+ */
+void                sc_statistics_add (sc_statistics_t * stats,
+                                       const char *name);
+
+/** Register a statistics variable by name and set its count to 0.
+ * This variable must not exist already.
+ */
+void                sc_statistics_add_empty (sc_statistics_t * stats,
+                                             const char *name);
+
+/** Set the value of a statistics variable, see sc_stats_set1.
+ * The variable must previously be added with sc_statistics_add.
+ * This assumes count=1 as in the sc_stats_set1 function above.
+ */
+void                sc_statistics_set (sc_statistics_t * stats,
+                                       const char *name, double value);
+
+/** Add an instance of a statistics variable, see sc_stats_accumulate
+ * The variable must previously be added with sc_statistics_add_empty.
+ */
+void                sc_statistics_accumulate (sc_statistics_t * stats,
+                                              const char *name, double value);
+
+/** Compute statistics for all variables, see sc_stats_compute.
+ */
+void                sc_statistics_compute (sc_statistics_t * stats);
+
+/** Print all statistics variables, see sc_stats_print.
+ */
+void                sc_statistics_print (sc_statistics_t * stats,
+                                         int package_id, int log_priority,
+                                         int full, int summary);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_STATISTICS_H */
diff --git a/sc/src/sc_warp.c b/sc/src/sc_warp.c
new file mode 100644
index 0000000..70315bd
--- /dev/null
+++ b/sc/src/sc_warp.c
@@ -0,0 +1,221 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_warp.h>
+
+sc_warp_interval_t *
+sc_warp_new (double r_low, double r_high)
+{
+  sc_warp_interval_t *root;
+
+  SC_ASSERT (r_low <= r_high);
+
+  root = SC_ALLOC (sc_warp_interval_t, 1);
+  root->level = 0;
+  root->r_low = r_low;
+  root->r_high = r_high;
+  root->left = root->right = NULL;
+
+  return root;
+}
+
+void
+sc_warp_destroy (sc_warp_interval_t * root)
+{
+  if (root->left != NULL)
+    sc_warp_destroy (root->left);
+  if (root->right != NULL)
+    sc_warp_destroy (root->right);
+  SC_FREE (root);
+}
+
+static void
+sc_warp_update_interval (sc_warp_interval_t * iv,
+                         int start, int end, double *r_points,
+                         double r_tol, int rem_levels)
+{
+  int                 i_low, i_high, i_guess, i_best;
+  int                 i_left_end, i_right_start;
+  double              r, r_best, r_dist, r_sign, r_mid;
+
+  SC_ASSERT (0 <= start && start < end);
+  SC_ASSERT (r_points[start] >= iv->r_low);
+  SC_ASSERT (r_points[end - 1] <= iv->r_high);
+
+  SC_LDEBUGF ("Level %d interval %g %g with %d %d\n",
+              rem_levels, iv->r_low, iv->r_high, start, end);
+
+  while (start < end && r_points[start] <= iv->r_low)
+    ++start;
+  while (start < end && r_points[end - 1] >= iv->r_high)
+    --end;
+  if (start >= end || rem_levels == 0) {
+    return;
+  }
+
+  if (iv->left != NULL) {
+    SC_ASSERT (iv->right != NULL);
+    SC_ASSERT (iv->left->r_high == iv->right->r_low);   /* ignore warning */
+
+    /* find highest point with r < r_mid, which need not exist */
+    r_mid = iv->left->r_high;
+    i_low = start;
+    i_high = end - 1;
+    while (i_low < i_high) {
+      i_guess = (i_low + i_high + 1) / 2;
+      r = r_points[i_guess];
+      if (r < r_mid) {
+        i_low = i_guess;
+      }
+      else {
+        i_high = i_guess - 1;
+      }
+    }
+    SC_ASSERT (i_low == i_high);
+    SC_LDEBUGF ("Searched low %d %g\n", i_low, r_points[i_low]);
+    if (r_points[i_low] >= r_mid) {
+      /* left interval is empty */
+      i_left_end = start;
+    }
+    else {
+      i_left_end = i_low + 1;
+    }
+    while (i_high < end && r_points[i_high] <= r_mid) {
+      ++i_high;
+    }
+    i_right_start = i_high;
+  }
+  else {
+    /* find closest point to mid-interval */
+    r_sign = iv->r_high - iv->r_low;
+    r_best = r_mid = .5 * (iv->r_low + iv->r_high);
+    i_low = start;
+    i_high = end - 1;
+    i_guess = i_best = -1;
+    while (i_low <= i_high) {
+      i_guess = (i_low + i_high + 1) / 2;
+      r = r_points[i_guess];
+      r_dist = r - r_mid;
+      SC_LDEBUGF ("Search now %d %d with %d %g %g\n",
+                  i_low, i_high, i_guess, r, r_dist);
+      if (fabs (r_dist) < fabs (r_sign)) {
+        r_sign = r_dist;
+        r_best = r;
+        i_best = i_guess;
+      }
+      if (r_dist < 0.) {
+        i_low = i_guess + 1;
+      }
+      else if (r_dist > 0.) {
+        i_high = i_guess - 1;
+      }
+      else
+        break;
+    }
+    SC_ASSERT (i_guess >= start && i_guess < end);
+    SC_ASSERT (i_best >= start && i_best < end);
+    SC_LDEBUGF ("Searched %d %d with %d %g %g\n",
+                i_low, i_high, i_best, r_best, r_sign);
+
+    iv->left = SC_ALLOC (sc_warp_interval_t, 1);
+    iv->left->r_low = iv->r_low;
+    iv->left->level = iv->level + 1;
+    iv->left->left = iv->left->right = NULL;
+    iv->right = SC_ALLOC (sc_warp_interval_t, 1);
+    iv->right->r_high = iv->r_high;
+    iv->right->level = iv->level + 1;
+    iv->right->left = iv->right->right = NULL;
+
+    r_dist = r_tol * (iv->r_high - iv->r_low);
+    if (fabs (r_sign) < r_dist) {
+      SC_LDEBUG ("New matching point\n");
+
+      iv->left->r_high = iv->right->r_low = r_best;
+      i_left_end = i_best;
+      i_right_start = i_best + 1;
+    }
+    else {
+      SC_LDEBUGF ("No matching point error %g %g\n", fabs (r_sign), r_dist);
+
+      if (r_sign < 0) {
+        i_left_end = i_right_start = i_best + 1;
+        iv->left->r_high = iv->right->r_low = r_mid;    /* - r_dist; */
+      }
+      else {
+        i_left_end = i_right_start = i_best;
+        iv->left->r_high = iv->right->r_low = r_mid;    /* + r_dist; */
+      }
+    }
+  }
+
+  if (start < i_left_end)
+    sc_warp_update_interval (iv->left, start, i_left_end, r_points,
+                             r_tol, rem_levels - 1);
+  if (i_right_start < end)
+    sc_warp_update_interval (iv->right, i_right_start, end, r_points,
+                             r_tol, rem_levels - 1);
+}
+
+void
+sc_warp_update (sc_warp_interval_t * root, int num_points, double *r_points,
+                double r_tol, int max_level)
+{
+  if (num_points <= 0)
+    return;
+
+  SC_ASSERT (r_points != NULL);
+  SC_ASSERT (0 <= r_tol && r_tol <= 1.);
+
+  /* call interval recursion */
+  sc_warp_update_interval (root, 0, num_points, r_points, r_tol, max_level);
+}
+
+void
+sc_warp_write (sc_warp_interval_t * root, FILE * nout)
+{
+  if (root->left == NULL) {
+    SC_ASSERT (root->right == NULL);
+    fprintf (nout, "Warp interval level %d [%g %g] length %g\n",
+             root->level, root->r_low, root->r_high,
+             root->r_high - root->r_low);
+  }
+  else {
+    sc_warp_write (root->left, nout);
+    sc_warp_write (root->right, nout);
+  }
+}
+
+void
+sc_warp_print (int package_id, int log_priority, sc_warp_interval_t * root)
+{
+  if (root->left == NULL) {
+    SC_ASSERT (root->right == NULL);
+    SC_GEN_LOGF (package_id, SC_LC_GLOBAL, log_priority,
+                 "Warp interval level %d [%g %g] length %g\n",
+                 root->level, root->r_low, root->r_high,
+                 root->r_high - root->r_low);
+  }
+  else {
+    sc_warp_print (package_id, log_priority, root->left);
+    sc_warp_print (package_id, log_priority, root->right);
+  }
+}
diff --git a/sc/src/sc_warp.h b/sc/src/sc_warp.h
new file mode 100644
index 0000000..d962430
--- /dev/null
+++ b/sc/src/sc_warp.h
@@ -0,0 +1,56 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef SC_WARP_H
+#define SC_WARP_H
+
+#include <sc.h>
+
+typedef struct sc_warp_interval sc_warp_interval_t;
+
+struct sc_warp_interval
+{
+  int                 level;    /* level of root is 0 */
+  double              r_low, r_high;    /* interval coordinates */
+  sc_warp_interval_t *left, *right;     /* binary tree descendants */
+};
+
+SC_EXTERN_C_BEGIN;
+
+sc_warp_interval_t *sc_warp_new (double r_low, double r_high);
+void                sc_warp_destroy (sc_warp_interval_t * root);
+
+/** Refine the warp as necessary to accomodate a set of new points.
+ * \param [in] root             The root warp interval.
+ * \param [in] num_points       Number of new points to integrate.
+ * \param [in] r_points         The new points need to be sorted.
+ */
+void                sc_warp_update (sc_warp_interval_t * root,
+                                    int num_points, double *r_points,
+                                    double r_tol, int max_level);
+void                sc_warp_print (int package_id, int log_priority,
+                                   sc_warp_interval_t * root);
+void                sc_warp_write (sc_warp_interval_t * root, FILE * nout);
+
+SC_EXTERN_C_END;
+
+#endif /* !SC_WARP_H */
diff --git a/sc/test/Makefile.am b/sc/test/Makefile.am
new file mode 100644
index 0000000..3c01716
--- /dev/null
+++ b/sc/test/Makefile.am
@@ -0,0 +1,54 @@
+
+# This file is part of the SC Library
+# Makefile.am test
+# included non-recursively from toplevel directory
+
+sc_test_programs = \
+        test/sc_test_allgather \
+        test/sc_test_arrays \
+        test/sc_test_builtin \
+        test/sc_test_dmatrix \
+        test/sc_test_dmatrix_pool \
+        test/sc_test_io_sink \
+        test/sc_test_keyvalue \
+        test/sc_test_notify \
+        test/sc_test_reduce \
+        test/sc_test_search \
+        test/sc_test_sort \
+        test/sc_test_sortb
+## Reenable and properly verify pqueue when it is actually used
+##      test/sc_test_pqueue \
+
+check_PROGRAMS += $(sc_test_programs)
+
+test_sc_test_allgather_SOURCES = test/test_allgather.c
+test_sc_test_arrays_SOURCES = test/test_arrays.c
+test_sc_test_builtin_SOURCES = test/test_builtin.c
+test_sc_test_dmatrix_SOURCES = test/test_dmatrix.c
+test_sc_test_dmatrix_pool_SOURCES = test/test_dmatrix_pool.c
+test_sc_test_io_sink_SOURCES = test/test_io_sink.c
+test_sc_test_keyvalue_SOURCES = test/test_keyvalue.c
+test_sc_test_notify_SOURCES = test/test_notify.c
+## Reenable and properly verify pqueue when it is actually used
+## test_sc_test_pqueue_SOURCES = test/test_pqueue.c
+test_sc_test_reduce_SOURCES = test/test_reduce.c
+test_sc_test_search_SOURCES = test/test_search.c
+test_sc_test_sort_SOURCES = test/test_sort.c
+test_sc_test_sortb_SOURCES = test/test_sortb.c
+
+TESTS += $(sc_test_programs)
+
+LINT_CSOURCES += \
+        $(test_sc_test_allgather_SOURCES) \
+        $(test_sc_test_arrays_SOURCES) \
+        $(test_sc_test_builtin_SOURCES) \
+        $(test_sc_test_dmatrix_SOURCES) \
+        $(test_sc_test_dmatrix_pool_SOURCES) \
+        $(test_sc_test_io_sink_SOURCES) \
+        $(test_sc_test_keyvalue_SOURCES) \
+        $(test_sc_test_notify_SOURCES) \
+        $(test_sc_test_pqueue_SOURCES) \
+        $(test_sc_test_reduce_SOURCES) \
+        $(test_sc_test_search_SOURCES) \
+        $(test_sc_test_sort_SOURCES) \
+        $(test_sc_test_sortb_SOURCES)
diff --git a/sc/test/test_allgather.c b/sc/test/test_allgather.c
new file mode 100644
index 0000000..c96447e
--- /dev/null
+++ b/sc/test/test_allgather.c
@@ -0,0 +1,132 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_allgather.h>
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 mpisize;
+  int                 mpirank;
+  int                 i;
+  int                *idata;
+  double              elapsed_alltoall = 0.;
+  double              elapsed_recursive;
+  double              dsend;
+  double             *ddata1;
+  double             *ddata2;
+  double              elapsed_allgather;
+  double              elapsed_replacement;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+
+  idata = SC_ALLOC (int, mpisize);
+
+  if (mpisize <= 64) {
+    SC_GLOBAL_INFO ("Testing sc_allgather_alltoall\n");
+
+    for (i = 0; i < mpisize; ++i) {
+      idata[i] = (i == mpirank) ? mpirank : -1;
+    }
+    elapsed_alltoall = -sc_MPI_Wtime ();
+    sc_allgather_alltoall (mpicomm, (char *) idata, (int) sizeof (int),
+                           mpisize, mpirank, mpirank);
+    elapsed_alltoall += sc_MPI_Wtime ();
+    for (i = 0; i < mpisize; ++i) {
+      SC_ASSERT (idata[i] == i);
+    }
+  }
+
+  SC_GLOBAL_INFO ("Testing sc_allgather_recursive\n");
+
+  for (i = 0; i < mpisize; ++i) {
+    idata[i] = (i == mpirank) ? mpirank : -1;
+  }
+  elapsed_recursive = -sc_MPI_Wtime ();
+  sc_allgather_recursive (mpicomm, (char *) idata, (int) sizeof (int),
+                          mpisize, mpirank, mpirank);
+  elapsed_recursive += sc_MPI_Wtime ();
+  for (i = 0; i < mpisize; ++i) {
+    SC_ASSERT (idata[i] == i);
+  }
+
+  SC_FREE (idata);
+
+  ddata1 = SC_ALLOC (double, mpisize);
+  ddata2 = SC_ALLOC (double, mpisize);
+
+  SC_GLOBAL_INFO ("Testing allgather and replacement\n");
+
+  dsend = M_PI;
+
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_allgather = -sc_MPI_Wtime ();
+  mpiret = sc_MPI_Allgather (&dsend, 1, sc_MPI_DOUBLE, ddata1, 1,
+                             sc_MPI_DOUBLE, mpicomm);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_allgather += sc_MPI_Wtime ();
+
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_replacement = -sc_MPI_Wtime ();
+  mpiret = sc_allgather (&dsend, 1, sc_MPI_DOUBLE, ddata2, 1, sc_MPI_DOUBLE,
+                         mpicomm);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_replacement += sc_MPI_Wtime ();
+
+  for (i = 0; i < mpisize; ++i) {
+    SC_ASSERT (ddata1[i] == ddata2[i]); /* exact match wanted */
+  }
+  SC_ASSERT (ddata1[mpirank] == dsend); /* exact match wanted */
+
+  SC_FREE (ddata1);
+  SC_FREE (ddata2);
+
+  SC_GLOBAL_STATISTICSF ("Timings with threshold %d on %d cores\n",
+                         SC_AG_ALLTOALL_MAX, mpisize);
+  SC_GLOBAL_STATISTICSF ("   alltoall %g\n", elapsed_alltoall);
+  SC_GLOBAL_STATISTICSF ("   recursive %g\n", elapsed_recursive);
+  SC_GLOBAL_STATISTICSF ("   allgather %g\n", elapsed_allgather);
+  SC_GLOBAL_STATISTICSF ("   replacement %g\n", elapsed_replacement);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/test/test_arrays.c b/sc/test/test_arrays.c
new file mode 100644
index 0000000..7d1720c
--- /dev/null
+++ b/sc/test/test_arrays.c
@@ -0,0 +1,231 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_containers.h>
+
+static              ssize_t
+sc_array_bsearch_range (sc_array_t * array, size_t begin, size_t end,
+                        const void *key, int (*compar) (const void *,
+                                                        const void *))
+{
+  ssize_t             result;
+  sc_array_t         *view;
+
+  view = sc_array_new_view (array, begin, end - begin);
+  result = sc_array_bsearch (view, key, compar);
+  sc_array_destroy (view);
+
+  return result < 0 ? result : (ssize_t) begin + result;
+}
+
+static void
+test_new_size (sc_array_t * a)
+{
+  const size_t        S = a->elem_size;
+  const size_t        N = a->elem_count;
+  sc_array_t         *v, *w;
+
+  v = sc_array_new_size (S, N);
+  SC_CHECK_ABORT (v->elem_size == S && S == sizeof (int), "Size mismatch");
+  SC_CHECK_ABORT (v->elem_count == N && N > 0, "Count mismatch");
+  SC_CHECK_ABORT (v->byte_alloc <= a->byte_alloc, "Alloc mismatch");
+
+  w = sc_array_new (S);
+  sc_array_copy (w, v);
+  SC_CHECK_ABORT (sc_array_is_equal (v, w), "Array mismatch");
+  sc_array_destroy (w);
+
+  memcpy (v->array, a->array, N * S);
+  SC_CHECK_ABORT (sc_array_is_sorted (v, sc_int_compare), "Sort failed");
+  sc_array_destroy (v);
+}
+
+static void
+test_new_view (sc_array_t * a)
+{
+  const size_t        N = a->elem_count;
+  sc_array_t         *v;
+
+  v = sc_array_new_view (a, 0, N);
+  SC_CHECK_ABORT (sc_array_is_sorted (v, sc_int_compare), "Sort failed");
+  sc_array_destroy (v);
+
+  v = sc_array_new_view (a, N / 2, N / 2);
+  SC_CHECK_ABORT (sc_array_is_sorted (v, sc_int_compare), "Sort failed");
+  sc_array_destroy (v);
+
+  v = sc_array_new_view (a, N / 5, 3 * N / 4);
+  SC_CHECK_ABORT (sc_array_is_sorted (v, sc_int_compare), "Sort failed");
+  sc_array_destroy (v);
+}
+
+static void
+test_new_data (sc_array_t * a)
+{
+  const size_t        s = a->elem_size;
+  const size_t        N = a->elem_count;
+  sc_array_t         *v;
+
+  v = sc_array_new_data (a->array, s, N);
+  SC_CHECK_ABORT (sc_array_is_sorted (v, sc_int_compare), "Sort failed");
+  sc_array_destroy (v);
+
+  v = sc_array_new_data (a->array + s * (N / 2), s, N / 2);
+  SC_CHECK_ABORT (sc_array_is_sorted (v, sc_int_compare), "Sort failed");
+  sc_array_destroy (v);
+
+  v = sc_array_new_data (a->array + s * (N / 5), s, 3 * N / 4);
+  SC_CHECK_ABORT (sc_array_is_sorted (v, sc_int_compare), "Sort failed");
+  sc_array_destroy (v);
+}
+
+int
+main (int argc, char **argv)
+{
+  const int           N = 29;
+  int                 i, j, s, c;
+  int                *pe;
+  size_t              b1, b2;
+  ssize_t             result, r1, r2, r3, t;
+  sc_array_t         *a, *p;
+  size_t             *perm;
+  int                *data;
+
+  sc_init (sc_MPI_COMM_NULL, 1, 1, NULL, SC_LP_DEFAULT);
+
+  a = sc_array_new (sizeof (int));
+  sc_array_resize (a, (size_t) N);
+
+  SC_GLOBAL_INFOF ("Sizeof size_t %lld long %lld sc_array_t %lld\n",
+                   (long long) sizeof (size_t),
+                   (long long) sizeof (long),
+                   (long long) sizeof (sc_array_t));
+  SC_GLOBAL_INFOF ("Array byte size %lld\n", (long long)
+                   sc_array_memory_used (a, 1));
+
+  for (i = 0; i < N; ++i) {
+    pe = (int *) sc_array_index_int (a, i);
+    SC_CHECK_ABORT (sc_array_position (a, pe) == (size_t) i,
+                    "Position failed");
+
+    *pe = (i + N / 2) * (N - i);        /* can create duplicates */
+  }
+  sc_array_sort (a, sc_int_compare);
+  SC_CHECK_ABORT (sc_array_is_sorted (a, sc_int_compare), "Sort failed");
+  for (i = 0; i < N; ++i) {
+    s = (i + N / 2) * (N - i);
+    result = sc_array_bsearch (a, &s, sc_int_compare);
+    SC_CHECK_ABORT (0 <= result && result < (ssize_t) N, "Result failed");
+  }
+
+  test_new_size (a);
+  test_new_view (a);
+  test_new_data (a);
+
+  for (i = 0; i < N; ++i) {
+    pe = (int *) sc_array_index_int (a, i);
+    *pe = 1 + i + i * i;        /* is already sorted */
+  }
+  for (i = 0; i < N; ++i) {
+    s = 1 + i + i * i;
+
+    b1 = b2 = (size_t) i;
+    result = sc_array_bsearch_range (a, b1, b2, &s, sc_int_compare);
+    SC_CHECK_ABORT (result == -1, "Empty range failed");
+
+    b1 = (size_t) (N / 2);
+    b2 = (size_t) (3 * N / 4);
+    r1 = sc_array_bsearch_range (a, 0, b1, &s, sc_int_compare);
+    r2 = sc_array_bsearch_range (a, b1, b2, &s, sc_int_compare);
+    r3 = sc_array_bsearch_range (a, b2, (size_t) N, &s, sc_int_compare);
+
+    c = 0;
+    t = -1;
+    if (r1 < 0)
+      ++c;
+    else
+      t = r1;
+    if (r2 < 0)
+      ++c;
+    else
+      t = r2;
+    if (r3 < 0)
+      ++c;
+    else
+      t = r3;
+    SC_CHECK_ABORT (c == 2 && t == (ssize_t) i, "Combined ranges failed");
+  }
+
+  sc_array_destroy (a);
+
+  /* test permute in place */
+
+  data = SC_ALLOC (int, 2 * N);
+
+  memset (data, -1, 2 * N * sizeof (int));
+
+  /* create permuted pairs */
+
+  data[0] = 0;
+  data[1] = 1;
+  for (i = 1; i < N; i++) {
+    j = (int) (rand () % (i + 1));
+    data[2 * i] = data[2 * j];
+    data[2 * i + 1] = data[2 * j + 1];
+    data[2 * j] = 2 * i;
+    data[2 * j + 1] = 2 * i + 1;
+  }
+
+  for (i = 0; i < N; i++) {
+    SC_ASSERT (data[2 * i] >= 0);
+    SC_ASSERT (data[2 * i + 1] >= 0);
+    SC_ASSERT (data[2 * i + 1] - data[2 * i] == 1);
+  }
+
+  a = sc_array_new_data (data, 2 * sizeof (int), (size_t) N);
+
+  perm = SC_ALLOC (size_t, N);
+
+  /* perm everything back to its proper place */
+
+  for (i = 0; i < N; i++) {
+    perm[i] = (size_t) data[2 * i] / 2;
+  }
+
+  p = sc_array_new_data (perm, sizeof (size_t), (size_t) N);
+  sc_array_permute (a, p, 0);
+
+  for (i = 0; i < N; i++) {
+    SC_CHECK_ABORT (data[2 * i] == 2 * i, "Permutation failure");
+    SC_CHECK_ABORT (data[2 * i + 1] == 2 * i + 1, "Permutation failure");
+    SC_CHECK_ABORT ((int) perm[i] == i, "Permutation failure");
+  }
+
+  sc_array_destroy (p);
+  sc_array_destroy (a);
+  SC_FREE (perm);
+  SC_FREE (data);
+
+  sc_finalize ();
+
+  return 0;
+}
diff --git a/sc/test/test_builtin.c b/sc/test/test_builtin.c
new file mode 100644
index 0000000..dfabe98
--- /dev/null
+++ b/sc/test/test_builtin.c
@@ -0,0 +1,160 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+/* this test checks the possibly builtin third-party libraries */
+
+#include <sc_getopt.h>
+#include <sc_obstack.h>
+
+/* truthfully, the libraries below are not builtin anymore */
+#include <sc_config.h>
+#ifdef SC_HAVE_ZLIB
+#include <zlib.h>
+#endif
+#ifdef SC_HAVE_LUA
+#include <sc_lua.h>
+#endif
+
+static int
+test_getopt (int argc, char **argv)
+{
+  int                 aflag, anint;
+  int                 copt, lindex;
+  const struct option sopt[] = {
+    {"add", 1, 0, 0},
+    {"append", 0, 0, 0},
+    {"delete", 1, 0, 0},
+    {"verbose", 0, 0, 0},
+    {"create", 1, 0, 'c'},
+    {"file", 1, 0, 0},
+    {0, 0, 0, 0}
+  };
+
+  aflag = 0;
+  anint = 0;
+  while ((copt = getopt_long (argc, argv, "cnt:", sopt, &lindex)) != -1) {
+    switch (copt) {
+    case 0:
+      printf ("option %s", sopt[lindex].name);
+      if (optarg)
+        printf (" with arg %s", optarg);
+      printf ("\n");
+      break;
+    case 'c':
+      break;
+    case 'n':
+      aflag = 1;
+      break;
+    case 't':
+      anint = atoi (optarg);
+      break;
+    default:
+      fprintf (stderr, "Usage: %s [-t integer] [-n] [-c]\n", argv[0]);
+      return 1;
+    }
+  }
+  if (anint == 1234567) {
+    fprintf (stderr, "Test with %d %d\n", aflag, anint);
+  }
+
+  return 0;
+}
+
+static int
+test_obstack (void)
+{
+  void               *mem;
+  struct obstack      obst;
+  /*@ignore@ */
+  static void        *(*obstack_chunk_alloc) (size_t) = malloc;
+  static void         (*obstack_chunk_free) (void *) = free;
+  /*@end@ */
+
+  obstack_init (&obst);
+  mem = obstack_alloc (&obst, 47);
+  mem = obstack_alloc (&obst, 47135);
+  mem = obstack_alloc (&obst, 473);
+  *(char *) mem = '\0';
+  obstack_free (&obst, NULL);
+
+  return 0;
+}
+
+#ifdef SC_HAVE_ZLIB
+
+static int
+test_zlib (void)
+{
+  const char          b1[] = "This is one string";
+  const char          b2[] = "This is another string";
+  const size_t        l1 = strlen (b1);
+  const size_t        l2 = strlen (b2);
+  char                b3[BUFSIZ];
+  uLong               adler0, adler1, adler2, adler3a, adler3b;
+
+  adler0 = adler32 (0L, Z_NULL, 0);
+  adler1 = adler32 (adler0, (const Bytef *) b1, l1);
+  adler2 = adler32 (adler0, (const Bytef *) b2, l2);
+  adler3a = adler32_combine (adler1, adler2, l2);
+
+  snprintf (b3, BUFSIZ, "%s%s", b1, b2);
+  adler3b = adler32 (adler0, (const Bytef *) b3, l1 + l2);
+
+  return adler3a != adler3b;
+}
+
+#endif /* SC_HAVE_ZLIB */
+
+#ifdef SC_HAVE_LUA
+
+static int
+test_lua (void)
+{
+  lua_State          *L = luaL_newstate ();
+
+  lua_close (L);
+
+  return 0;
+}
+
+#endif /* SC_HAVE_LUA */
+
+int
+main (int argc, char **argv)
+{
+  int                 num_errors = 0;
+
+  sc_init (sc_MPI_COMM_NULL, 1, 1, NULL, SC_LP_DEFAULT);
+
+  num_errors += test_getopt (argc, argv);
+  num_errors += test_obstack ();
+#ifdef SC_HAVE_ZLIB
+  num_errors += test_zlib ();
+#endif
+#ifdef SC_HAVE_LUA
+  num_errors += test_lua ();
+#endif
+
+  sc_finalize ();
+
+  return num_errors > 0;
+}
diff --git a/sc/test/test_dmatrix.c b/sc/test/test_dmatrix.c
new file mode 100644
index 0000000..f68a8d1
--- /dev/null
+++ b/sc/test/test_dmatrix.c
@@ -0,0 +1,151 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_dmatrix.h>
+
+#if defined(SC_WITH_BLAS) && defined(SC_WITH_LAPACK)
+
+static const double eps = 2.220446049250313e-16;
+
+static void
+test_zero_sizes (void)
+{
+  sc_dmatrix_t       *m1, *m2, *m3;
+
+  m1 = sc_dmatrix_new (0, 3);
+  sc_dmatrix_set_value (m1, -5.);
+
+  m2 = sc_dmatrix_clone (m1);
+  sc_dmatrix_fabs (m1, m2);
+  sc_dmatrix_resize (m2, 3, 0);
+
+  m3 = sc_dmatrix_new (0, 0);
+  sc_dmatrix_multiply (SC_NO_TRANS, SC_NO_TRANS, 1., m1, m2, 0., m3);
+
+  sc_dmatrix_destroy (m1);
+  sc_dmatrix_destroy (m2);
+  sc_dmatrix_destroy (m3);
+}
+
+#endif
+
+int
+main (int argc, char **argv)
+{
+  int                 num_failed_tests = 0;
+#if defined(SC_WITH_BLAS) && defined(SC_WITH_LAPACK)
+  int                 j;
+  int                 mpiret;
+  sc_dmatrix_t       *A, *x, *xexact, *b, *bT, *xT, *xTexact;
+  double              xmaxerror = 0.0;
+  double              A_data[] = { 8.0, 1.0, 6.0,
+    3.0, 5.0, 7.0,
+    4.0, 9.0, 2.0
+  };
+  double              b_data[] = { 1.0, 2.0, 3.0 };
+  double              xexact_data[] = { -0.1 / 3.0, 1.4 / 3.0, -0.1 / 3.0 };
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+
+  A = sc_dmatrix_new_data (3, 3, A_data);
+  b = sc_dmatrix_new_data (1, 3, b_data);
+  xexact = sc_dmatrix_new_data (1, 3, xexact_data);
+  x = sc_dmatrix_new (1, 3);
+
+  sc_dmatrix_rdivide (SC_NO_TRANS, b, A, x);
+
+  sc_dmatrix_add (-1.0, xexact, x);
+
+  xmaxerror = 0.0;
+  for (j = 0; j < 3; ++j) {
+    xmaxerror = SC_MAX (xmaxerror, fabs (x->e[0][j]));
+  }
+
+  SC_LDEBUGF ("xmaxerror = %g\n", xmaxerror);
+
+  if (xmaxerror > 100.0 * eps) {
+    ++num_failed_tests;
+  }
+
+  xexact->e[0][0] = 0.05;
+  xexact->e[0][1] = 0.3;
+  xexact->e[0][2] = 0.05;
+
+  sc_dmatrix_rdivide (SC_TRANS, b, A, x);
+
+  sc_dmatrix_add (-1.0, xexact, x);
+
+  xmaxerror = 0.0;
+  for (j = 0; j < 3; ++j) {
+    xmaxerror = SC_MAX (xmaxerror, fabs (x->e[0][j]));
+  }
+
+  SC_LDEBUGF ("xmaxerror = %g\n", xmaxerror);
+
+  if (xmaxerror > 100.0 * eps) {
+    ++num_failed_tests;
+  }
+
+  bT = sc_dmatrix_new_data (3, 1, b_data);
+  xT = sc_dmatrix_new (3, 1);
+  xTexact = sc_dmatrix_new (3, 1);
+
+  xTexact->e[0][0] = 0.05;
+  xTexact->e[1][0] = 0.3;
+  xTexact->e[2][0] = 0.05;
+
+  sc_dmatrix_ldivide (SC_NO_TRANS, A, bT, xT);
+
+  sc_dmatrix_add (-1.0, xTexact, xT);
+
+  xmaxerror = 0.0;
+  for (j = 0; j < 3; ++j) {
+    xmaxerror = SC_MAX (xmaxerror, fabs (xT->e[0][j]));
+  }
+
+  SC_LDEBUGF ("xTmaxerror = %g\n", xmaxerror);
+
+  if (xmaxerror > 100.0 * eps) {
+    ++num_failed_tests;
+  }
+
+  sc_dmatrix_destroy (xTexact);
+  sc_dmatrix_destroy (xT);
+  sc_dmatrix_destroy (bT);
+  sc_dmatrix_destroy (A);
+  sc_dmatrix_destroy (b);
+  sc_dmatrix_destroy (x);
+  sc_dmatrix_destroy (xexact);
+
+  test_zero_sizes ();
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+#endif
+
+  return num_failed_tests;
+}
diff --git a/sc/test/test_dmatrix_pool.c b/sc/test/test_dmatrix_pool.c
new file mode 100644
index 0000000..59aabd1
--- /dev/null
+++ b/sc/test/test_dmatrix_pool.c
@@ -0,0 +1,70 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_dmatrix.h>
+
+int
+main (int argc, char **argv)
+{
+#ifdef SC_WITH_BLAS
+  int                 mpiret;
+  sc_dmatrix_pool_t  *p13, *p92;
+  sc_dmatrix_t       *m1, *m2, *m3, *m4;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+
+  p13 = sc_dmatrix_pool_new (1, 3);
+  p92 = sc_dmatrix_pool_new (9, 2);
+
+  m1 = sc_dmatrix_pool_alloc (p13);
+  m2 = sc_dmatrix_pool_alloc (p92);
+  m3 = sc_dmatrix_pool_alloc (p13);
+
+  sc_dmatrix_pool_free (p13, m1);
+  m1 = sc_dmatrix_pool_alloc (p13);
+
+  m4 = sc_dmatrix_pool_alloc (p13);
+  sc_dmatrix_pool_free (p13, m1);
+
+  sc_dmatrix_pool_free (p13, m4);
+  m4 = sc_dmatrix_pool_alloc (p13);
+  m1 = sc_dmatrix_pool_alloc (p13);
+
+  sc_dmatrix_pool_free (p13, m1);
+  sc_dmatrix_pool_free (p92, m2);
+  sc_dmatrix_pool_free (p13, m3);
+  sc_dmatrix_pool_free (p13, m4);
+
+  sc_dmatrix_pool_destroy (p13);
+  sc_dmatrix_pool_destroy (p92);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+#endif /* !SC_WITH_BLAS */
+
+  return 0;
+}
diff --git a/sc/test/test_io_sink.c b/sc/test/test_io_sink.c
new file mode 100644
index 0000000..b8b9787
--- /dev/null
+++ b/sc/test/test_io_sink.c
@@ -0,0 +1,96 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_io.h>
+#include <sc_options.h>
+
+void
+the_test (const char *filename)
+{
+  int                 retval;
+  size_t              bytes_in, bytes_out;
+  const char          input[] =
+    "This is a string for sinking and sourcing.\n";
+  sc_array_t         *buffer;
+  sc_io_sink_t       *sink;
+
+  buffer = NULL;
+  if (filename == NULL) {
+    buffer = sc_array_new (sizeof (char));
+    sink = sc_io_sink_new (SC_IO_TYPE_BUFFER, SC_IO_MODE_WRITE,
+                           SC_IO_ENCODE_NONE, buffer);
+  }
+  else {
+    sink = sc_io_sink_new (SC_IO_TYPE_FILENAME, SC_IO_MODE_WRITE,
+                           SC_IO_ENCODE_NONE, filename);
+  }
+  SC_CHECK_ABORT (sink != NULL, "Sink create");
+
+  retval = sc_io_sink_write (sink, input, strlen (input));
+  SC_CHECK_ABORT (retval == 0, "Sink write");
+
+  retval = sc_io_sink_complete (sink, &bytes_in, &bytes_out);
+  SC_CHECK_ABORT (retval == 0, "Sink complete");
+  SC_GLOBAL_INFOF ("Bytes in %lld out %lld\n",
+                   (long long) bytes_in, (long long) bytes_out);
+
+  retval = sc_io_sink_destroy (sink);
+  SC_CHECK_ABORT (retval == 0, "Sink destroy");
+
+  if (filename == NULL) {
+    sc_array_destroy (buffer);
+  }
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 first;
+  const char         *filename;
+  sc_options_t       *opt;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+
+  opt = sc_options_new (argv[0]);
+  sc_options_add_string (opt, 'f', "filename", &filename, NULL,
+                         "File to write");
+  first = sc_options_parse (sc_package_id, SC_LP_INFO, opt, argc, argv);
+  if (first < 0) {
+    sc_options_print_usage (sc_package_id, SC_LP_INFO, opt, NULL);
+    sc_abort_collective ("Usage error");
+  }
+
+  if (sc_is_root ()) {
+    the_test (filename);
+  }
+
+  sc_options_destroy (opt);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/test/test_keyvalue.c b/sc/test/test_keyvalue.c
new file mode 100644
index 0000000..019b618
--- /dev/null
+++ b/sc/test/test_keyvalue.c
@@ -0,0 +1,221 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_keyvalue.h>
+
+int
+main (int argc, char **argv)
+{
+  int                 num_failed_tests = 0;
+
+  int                 mpiret;
+
+  sc_keyvalue_t      *args;
+  sc_keyvalue_t      *args2;
+
+  const char         *dummy = "I am a dummy string";
+  const char         *wrong = "I am the wrong string";
+  const char         *again = "Try this all over again";
+
+  int                 intTest;
+  double              doubleTest;
+  const char         *stringTest;
+  void               *pointerTest;
+
+  /* Initialization stuff */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+
+  /* Print some funny stuff */
+  SC_GLOBAL_LDEBUGF ("Hash of empty string: %x\n",
+                     sc_hash_function_string ("", NULL));
+  SC_GLOBAL_LDEBUGF ("Hash of ABCDEFGHIJKL: %x\n",
+                     sc_hash_function_string ("ABCDEFGHIJKL", NULL));
+  SC_GLOBAL_LDEBUGF ("Hash of dummy: %x\n",
+                     sc_hash_function_string (dummy, NULL));
+  SC_GLOBAL_LDEBUGF ("Hash of dummy: %x\n",
+                     sc_hash_function_string (wrong, NULL));
+  SC_GLOBAL_LDEBUGF ("Hash of dummy: %x\n",
+                     sc_hash_function_string (again, NULL));
+
+  /* Create a new argument set */
+  args = sc_keyvalue_newf (0,
+                           "i:intTest", -17,
+                           "g:doubleTest", 3.14159,
+                           "s:stringTest", "Hello Test!",
+                           "p:pointerTest", (void *) dummy, NULL);
+
+  intTest = sc_keyvalue_get_int (args, "intTest", 0);
+  doubleTest = sc_keyvalue_get_double (args, "doubleTest", 0.0);
+  stringTest = sc_keyvalue_get_string (args, "stringTest", wrong);
+  pointerTest = sc_keyvalue_get_pointer (args, "pointerTest", NULL);
+
+  if (intTest != -17) {
+    SC_VERBOSE ("Test 1 failure on int\n");
+    num_failed_tests++;
+  }
+  if (doubleTest != 3.14159) {
+    SC_VERBOSE ("Test 1 failure on double\n");
+    num_failed_tests++;
+  }
+  if (strcmp (stringTest, "Hello Test!")) {
+    SC_VERBOSE ("Test 1 failure on string\n");
+    num_failed_tests++;
+  }
+  if (pointerTest != (void *) dummy) {
+    SC_VERBOSE ("Test 1 failure on pointer\n");
+    num_failed_tests++;
+  }
+
+  sc_keyvalue_destroy (args);
+  args = NULL;
+
+  /* Create a new argument set using the sc_keyvalue_set functions */
+  args2 = sc_keyvalue_new ();
+
+  sc_keyvalue_set_int (args2, "intTest", -17);
+  sc_keyvalue_set_double (args2, "doubleTest", 3.14159);
+  sc_keyvalue_set_string (args2, "stringTest", "Hello Test!");
+  sc_keyvalue_set_pointer (args2, "pointerTest", (void *) dummy);
+
+  /* Direct verification that these objects now exist */
+  if (sc_keyvalue_exists (args2, "intTest") != SC_KEYVALUE_ENTRY_INT) {
+    SC_VERBOSE ("Test exist failure on int\n");
+    num_failed_tests++;
+  }
+  if (sc_keyvalue_exists (args2, "doubleTest") != SC_KEYVALUE_ENTRY_DOUBLE) {
+    SC_VERBOSE ("Test exist failure on double\n");
+    num_failed_tests++;
+  }
+  if (sc_keyvalue_exists (args2, "stringTest") != SC_KEYVALUE_ENTRY_STRING) {
+    SC_VERBOSE ("Test exist failure on string\n");
+    num_failed_tests++;
+  }
+  if (sc_keyvalue_exists (args2, "pointerTest") != SC_KEYVALUE_ENTRY_POINTER) {
+    SC_VERBOSE ("Test exist failure on pointer\n");
+    num_failed_tests++;
+  }
+
+  intTest = sc_keyvalue_get_int (args2, "intTest", 0);
+  doubleTest = sc_keyvalue_get_double (args2, "doubleTest", 0.0);
+  stringTest = sc_keyvalue_get_string (args2, "stringTest", wrong);
+  pointerTest = sc_keyvalue_get_pointer (args2, "pointerTest", NULL);
+
+  if (intTest != -17) {
+    SC_VERBOSE ("Test 2 failure on int\n");
+    num_failed_tests++;
+  }
+  if (doubleTest != 3.14159) {
+    SC_VERBOSE ("Test 2 failure on double\n");
+    num_failed_tests++;
+  }
+  if (strcmp (stringTest, "Hello Test!")) {
+    SC_VERBOSE ("Test 2 failure on string\n");
+    num_failed_tests++;
+  }
+  if (pointerTest != (void *) dummy) {
+    SC_VERBOSE ("Test 2 failure on pointer\n");
+    num_failed_tests++;
+  }
+
+  /* Test the unset functionality */
+  if (sc_keyvalue_unset (args2, "intTest") != SC_KEYVALUE_ENTRY_INT) {
+    SC_VERBOSE ("Test unset failure on int\n");
+    num_failed_tests++;
+  }
+  if (sc_keyvalue_unset (args2, "doubleTest") != SC_KEYVALUE_ENTRY_DOUBLE) {
+    SC_VERBOSE ("Test unset failure on double\n");
+    num_failed_tests++;
+  }
+  if (sc_keyvalue_unset (args2, "stringTest") != SC_KEYVALUE_ENTRY_STRING) {
+    SC_VERBOSE ("Test unset failure on string\n");
+    num_failed_tests++;
+  }
+  if (sc_keyvalue_unset (args2, "pointerTest") != SC_KEYVALUE_ENTRY_POINTER) {
+    SC_VERBOSE ("Test unset failure on pointer\n");
+    num_failed_tests++;
+  }
+
+  intTest = sc_keyvalue_get_int (args2, "intTest", 12);
+  doubleTest = sc_keyvalue_get_double (args2, "doubleTest", 2.71828);
+  stringTest =
+    sc_keyvalue_get_string (args2, "stringTest", "Another test string?");
+  pointerTest =
+    sc_keyvalue_get_pointer (args2, "pointerTest", (void *) again);
+
+  if (intTest != 12) {
+    SC_VERBOSE ("Test 3 failure on int\n");
+    num_failed_tests++;
+  }
+  if (doubleTest != 2.71828) {
+    SC_VERBOSE ("Test 3 failure on double\n");
+    num_failed_tests++;
+  }
+  if (strcmp (stringTest, "Another test string?")) {
+    SC_VERBOSE ("Test 3 failure on string\n");
+    num_failed_tests++;
+  }
+  if (pointerTest != again) {
+    SC_VERBOSE ("Test 3 failure on pointer\n");
+    num_failed_tests++;
+  }
+
+  /* Direct verification that these objects no longer exist */
+  if (sc_keyvalue_exists (args2, "intTest")) {
+    SC_VERBOSE ("Test 4 failure on int\n");
+    num_failed_tests++;
+  }
+  if (sc_keyvalue_exists (args2, "doubleTest")) {
+    SC_VERBOSE ("Test 4 failure on double\n");
+    num_failed_tests++;
+  }
+  if (sc_keyvalue_exists (args2, "stringTest")) {
+    SC_VERBOSE ("Test 4 failure on string\n");
+    num_failed_tests++;
+  }
+  if (sc_keyvalue_exists (args2, "pointerTest")) {
+    SC_VERBOSE ("Test 4 failure on pointer\n");
+    num_failed_tests++;
+  }
+
+  /* Test empty cases for exists and unset */
+  if (sc_keyvalue_exists (args2, "notakey") != SC_KEYVALUE_ENTRY_NONE) {
+    SC_VERBOSE ("Test failure on nonexist 1\n");
+    num_failed_tests++;
+  }
+  if (sc_keyvalue_unset (args2, "notanotherkey") != SC_KEYVALUE_ENTRY_NONE) {
+    SC_VERBOSE ("Test failure on nonexist 2\n");
+    num_failed_tests++;
+  }
+
+  sc_keyvalue_destroy (args2);
+
+  /* Shutdown procedures */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return num_failed_tests ? 1 : 0;
+}
diff --git a/sc/test/test_notify.c b/sc/test/test_notify.c
new file mode 100644
index 0000000..283b8ed
--- /dev/null
+++ b/sc/test/test_notify.c
@@ -0,0 +1,90 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_notify.h>
+
+int
+main (int argc, char **argv)
+{
+  int                 i;
+  int                 mpiret;
+  int                 mpisize, mpirank;
+  int                *senders, num_senders;
+  int                *senders2, num_senders2;
+  int                *receivers, num_receivers;
+  double              elapsed_allgather;
+  double              elapsed_native;
+  sc_MPI_Comm         mpicomm;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+
+  num_receivers = (mpirank * (mpirank % 100)) % 7;
+  num_receivers = SC_MIN (num_receivers, mpisize);
+  receivers = SC_ALLOC (int, num_receivers);
+  for (i = 0; i < num_receivers; ++i) {
+    receivers[i] = (3 * mpirank + i) % mpisize;
+  }
+  qsort (receivers, num_receivers, sizeof (int), sc_int_compare);
+
+  SC_GLOBAL_INFO ("Testing sc_notify_allgather\n");
+  senders = SC_ALLOC (int, mpisize);
+  elapsed_allgather = -sc_MPI_Wtime ();
+  mpiret = sc_notify_allgather (receivers, num_receivers,
+                                senders, &num_senders, mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_allgather += sc_MPI_Wtime ();
+
+  SC_GLOBAL_INFO ("Testing native sc_notify\n");
+  senders2 = SC_ALLOC (int, mpisize);
+  elapsed_native = -sc_MPI_Wtime ();
+  mpiret = sc_notify (receivers, num_receivers,
+                      senders2, &num_senders2, mpicomm);
+  SC_CHECK_MPI (mpiret);
+  elapsed_native += sc_MPI_Wtime ();
+
+  SC_CHECK_ABORT (num_senders == num_senders2, "Mismatched sender numbers");
+  for (i = 0; i < num_senders; ++i) {
+    SC_CHECK_ABORTF (senders[i] == senders2[i], "Mismatched sender %d", i);
+  }
+
+  SC_FREE (receivers);
+  SC_FREE (senders);
+  SC_FREE (senders2);
+
+  SC_GLOBAL_STATISTICSF ("   notify_allgather %g\n", elapsed_allgather);
+  SC_GLOBAL_STATISTICSF ("   notify           %g\n", elapsed_native);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/test/test_pqueue.c b/sc/test/test_pqueue.c
new file mode 100644
index 0000000..6587fac
--- /dev/null
+++ b/sc/test/test_pqueue.c
@@ -0,0 +1,155 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_containers.h>
+
+/* #define THEBIGTEST */
+
+static int
+compar (const void *p1, const void *p2)
+{
+  int                 i1 = *(int *) p1;
+  int                 i2 = *(int *) p2;
+
+  return i1 - i2;
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 i, i1, i2, i3, i3last, i4, i4last, temp, count;
+  size_t              s, swaps1, swaps2, swaps3, total1, total2, total3;
+  ssize_t             searched;
+  int                *pi;
+  sc_array_t         *a1, *a2, *a3, *a4;
+  int                 mpiret;
+  double              start, elapsed_pqueue, elapsed_qsort;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+
+  a1 = sc_array_new (sizeof (int));
+  a2 = sc_array_new (sizeof (int));
+  a3 = sc_array_new (sizeof (int));
+  a4 = sc_array_new (sizeof (int));
+
+#ifdef THEBIGTEST
+  count = 325323;
+#else
+  count = 3251;
+#endif
+  SC_INFOF ("Test pqueue with count %d\n", count);
+
+  start = -sc_MPI_Wtime ();
+
+  swaps1 = swaps2 = swaps3 = 0;
+  total1 = total2 = total3 = 0;
+  for (i = 0; i < count; ++i) {
+    *(int *) sc_array_push (a1) = i;
+    s = sc_array_pqueue_add (a1, &temp, compar);
+    swaps1 += ((s > 0) ? 1 : 0);
+    total1 += s;
+
+    *(int *) sc_array_push (a2) = count - i - 1;
+    s = sc_array_pqueue_add (a2, &temp, compar);
+    swaps2 += ((s > 0) ? 1 : 0);
+    total2 += s;
+
+    *(int *) sc_array_push (a3) = (15 * i) % 172;
+    s = sc_array_pqueue_add (a3, &temp, compar);
+    swaps3 += ((s > 0) ? 1 : 0);
+    total3 += s;
+  }
+  SC_CHECK_ABORT (swaps1 == 0 && total1 == 0, "pqueue_add");
+  SC_VERBOSEF ("   Swaps %lld %lld %lld Total %lld %lld %lld\n",
+               (long long) swaps1, (long long) swaps2, (long long) swaps3,
+               (long long) total1, (long long) total2, (long long) total3);
+
+  temp = 52;
+  searched = sc_array_bsearch (a1, &temp, compar);
+  SC_CHECK_ABORT (searched != -1, "array_bsearch_index");
+  pi = (int *) sc_array_index_ssize_t (a1, searched);
+  SC_CHECK_ABORT (*pi == temp, "array_bsearch");
+
+  i3last = -1;
+  swaps1 = swaps2 = swaps3 = 0;
+  total1 = total2 = total3 = 0;
+  for (i = 0; i < count; ++i) {
+    s = sc_array_pqueue_pop (a1, &i1, compar);
+    swaps1 += ((s > 0) ? 1 : 0);
+    total1 += s;
+
+    s = sc_array_pqueue_pop (a2, &i2, compar);
+    swaps2 += ((s > 0) ? 1 : 0);
+    total2 += s;
+
+    s = sc_array_pqueue_pop (a3, &i3, compar);
+    swaps3 += ((s > 0) ? 1 : 0);
+    total3 += s;
+
+    SC_CHECK_ABORT (i == i1 && i == i2, "pqueue_pop");
+    SC_CHECK_ABORT (i3 >= i3last, "pqueue_pop");
+    i3last = i3;
+  }
+  SC_VERBOSEF ("   Swaps %lld %lld %lld Total %lld %lld %lld\n",
+               (long long) swaps1, (long long) swaps2, (long long) swaps3,
+               (long long) total1, (long long) total2, (long long) total3);
+
+  elapsed_pqueue = start + sc_MPI_Wtime ();
+
+  sc_array_destroy (a1);
+  sc_array_destroy (a2);
+  sc_array_destroy (a3);
+
+  SC_INFOF ("Test array sort with count %d\n", count);
+
+  start = -sc_MPI_Wtime ();
+
+  /* the resize is done to be comparable with the above procedure */
+  for (i = 0; i < count; ++i) {
+    *(int *) sc_array_push (a4) = (15 * i) % 172;
+  }
+  sc_array_sort (a4, compar);
+
+  i4last = -1;
+  for (i = 0; i < count; ++i) {
+    i4 = *(int *) sc_array_index_int (a4, i);
+
+    SC_CHECK_ABORT (i4 >= i4last, "array_sort");
+    i4last = i4;
+  }
+  sc_array_resize (a4, 0);
+
+  elapsed_qsort = start + sc_MPI_Wtime ();
+  SC_STATISTICSF ("Test timings pqueue %g qsort %g\n",
+                  elapsed_pqueue, 3. * elapsed_qsort);
+
+  sc_array_destroy (a4);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/test/test_reduce.c b/sc/test/test_reduce.c
new file mode 100644
index 0000000..0bd6436
--- /dev/null
+++ b/sc/test/test_reduce.c
@@ -0,0 +1,109 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_reduce.h>
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 mpirank, mpisize;
+  int                 i, j;
+  char                cvalue, cresult;
+  int                 ivalue, iresult;
+  unsigned short      usvalue, usresult;
+  long                lvalue, lresult;
+  float               fvalue[3], fresult[3], fexpect[3];
+  double              dvalue, dresult;
+  sc_MPI_Comm         mpicomm;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+
+  /* test allreduce int max */
+  ivalue = mpirank;
+  sc_allreduce (&ivalue, &iresult, 1, sc_MPI_INT, sc_MPI_MAX, mpicomm);
+  SC_CHECK_ABORT (iresult == mpisize - 1, "Allreduce mismatch");
+
+  /* test reduce float max */
+  fvalue[0] = (float) mpirank;
+  fexpect[0] = (float) (mpisize - 1);
+  fvalue[1] = (float) (mpirank % 9 - 4);
+  fexpect[1] = (float) (mpisize >= 9 ? 4 : (mpisize - 1) % 9 - 4);
+  fvalue[2] = (float) (mpirank % 6);
+  fexpect[2] = (float) (mpisize >= 6 ? 5 : (mpisize - 1) % 6);
+  for (i = 0; i < mpisize; ++i) {
+    sc_reduce (fvalue, fresult, 3, sc_MPI_FLOAT, sc_MPI_MAX, i, mpicomm);
+    if (i == mpirank) {
+      for (j = 0; j < 3; ++j) {
+        SC_CHECK_ABORTF (fresult[j] == fexpect[j],      /* ok */
+                         "Reduce mismatch in %d", j);
+      }
+    }
+  }
+
+  /* test allreduce char min */
+  cvalue = (char) (mpirank % 127);
+  sc_allreduce (&cvalue, &cresult, 1, sc_MPI_CHAR, sc_MPI_MIN, mpicomm);
+  SC_CHECK_ABORT (cresult == 0, "Allreduce mismatch");
+
+  /* test reduce unsigned short min */
+  usvalue = (unsigned short) (mpirank % 32767);
+  for (i = 0; i < mpisize; ++i) {
+    sc_reduce (&usvalue, &usresult, 1, sc_MPI_UNSIGNED_SHORT, sc_MPI_MIN, i,
+               mpicomm);
+    if (i == mpirank) {
+      SC_CHECK_ABORT (usresult == 0, "Reduce mismatch");
+    }
+  }
+
+  /* test allreduce long sum */
+  lvalue = (long) mpirank;
+  sc_allreduce (&lvalue, &lresult, 1, sc_MPI_LONG, sc_MPI_SUM, mpicomm);
+  SC_CHECK_ABORT (lresult == ((long) (mpisize - 1)) * mpisize / 2,
+                  "Allreduce mismatch");
+
+  /* test reduce double sum */
+  dvalue = (double) mpirank;
+  for (i = 0; i < mpisize; ++i) {
+    sc_reduce (&dvalue, &dresult, 1, sc_MPI_DOUBLE, sc_MPI_SUM, i, mpicomm);
+    if (i == mpirank) {
+      SC_CHECK_ABORT (dresult == ((double) (mpisize - 1)) * mpisize / 2.,       /* ok */
+                      "Reduce mismatch");
+    }
+  }
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/test/test_search.c b/sc/test/test_search.c
new file mode 100644
index 0000000..fa5e4aa
--- /dev/null
+++ b/sc/test/test_search.c
@@ -0,0 +1,62 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_search.h>
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 mpirank, mpisize;
+  int                 maxlevel, level, target;
+  int                 i, position;
+  sc_MPI_Comm         mpicomm;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  if (mpirank == 0) {
+    maxlevel = 3;
+    target = 3;
+    for (level = maxlevel; level >= 0; --level) {
+      SC_LDEBUGF ("Level %d %d\n", maxlevel, level);
+
+      for (i = 0; i < 1 << level; ++i) {
+
+        position = sc_search_bias (maxlevel, level, i, target);
+        SC_LDEBUGF ("Levels %d %d index %d target %d position %d\n",
+                    maxlevel, level, i, target, position);
+      }
+    }
+  }
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/sc/test/test_sort.c b/sc/test/test_sort.c
new file mode 100644
index 0000000..34daa1f
--- /dev/null
+++ b/sc/test/test_sort.c
@@ -0,0 +1,133 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_allgather.h>
+#include <sc_sort.h>
+
+int
+main (int argc, char **argv)
+{
+#ifdef SC_DEBUG
+  int                 mpiret;
+  int                 rank, num_procs;
+  int                 i, isizet;
+  int                 k, printed;
+  int                *recvc, *displ;
+  int                 timing;
+  size_t              zz;
+  size_t              lcount, gtotal;
+  size_t             *nmemb;
+  double             *ldata, *gdata;
+  sc_MPI_Comm         mpicomm;
+  char                buffer[BUFSIZ];
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+
+  if (argc >= 2) {
+    timing = 1;
+    lcount = (size_t) strtol (argv[1], NULL, 0);
+  }
+  else {
+    timing = 0;
+    srand ((unsigned) rank << 15);
+    lcount = 8 + (size_t) (16. * rand () / (RAND_MAX + 1.0));
+  }
+
+  /* create partition information */
+  SC_INFOF ("Local values %d\n", (int) lcount);
+  nmemb = SC_ALLOC (size_t, num_procs);
+  isizet = (int) sizeof (size_t);
+  mpiret = sc_MPI_Allgather (&lcount, isizet, sc_MPI_BYTE,
+                             nmemb, isizet, sc_MPI_BYTE, mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  /* create data and sort it */
+  ldata = SC_ALLOC (double, lcount);
+  for (zz = 0; zz < lcount; ++zz) {
+    ldata[zz] = -50. + (100. * rand () / (RAND_MAX + 1.0));
+  }
+  sc_psort (mpicomm, ldata, nmemb, sizeof (double), sc_double_compare);
+
+  /* output result */
+  if (!timing) {
+    sleep ((unsigned) rank);
+    for (zz = 0; zz < lcount;) {
+      printed = 0;
+      for (k = 0; zz < lcount && k < 8; ++zz, ++k) {
+        printed += snprintf (buffer + printed, BUFSIZ - printed,
+                             "%8.3g", ldata[zz]);
+      }
+      SC_STATISTICSF ("%s\n", buffer);
+    }
+  }
+
+  /* verify result */
+  if (!timing || lcount < 1000) {
+    SC_GLOBAL_PRODUCTION ("Verifying\n");
+    gtotal = 0;
+    recvc = NULL;
+    displ = NULL;
+    gdata = NULL;
+    if (rank == 0) {
+      recvc = SC_ALLOC (int, num_procs);
+      displ = SC_ALLOC (int, num_procs + 1);
+      displ[0] = 0;
+      for (i = 0; i < num_procs; ++i) {
+        recvc[i] = (int) nmemb[i];
+        displ[i + 1] = displ[i] + recvc[i];
+      }
+      gtotal = (size_t) displ[num_procs];
+      gdata = SC_ALLOC (double, gtotal);
+    }
+    mpiret = sc_MPI_Gatherv (ldata, (int) lcount, sc_MPI_DOUBLE,
+                             gdata, recvc, displ, sc_MPI_DOUBLE, 0, mpicomm);
+    SC_CHECK_MPI (mpiret);
+    if (rank == 0) {
+      for (zz = 0; zz < gtotal - 1; ++zz) {
+        SC_CHECK_ABORT (gdata[zz] <= gdata[zz + 1], "Parallel sort failed");
+      }
+    }
+    SC_FREE (gdata);
+    SC_FREE (displ);
+    SC_FREE (recvc);
+  }
+
+  /* clean up and exit */
+  SC_FREE (ldata);
+  SC_FREE (nmemb);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+#endif
+
+  return 0;
+}
diff --git a/sc/test/test_sortb.c b/sc/test/test_sortb.c
new file mode 100644
index 0000000..802b422
--- /dev/null
+++ b/sc/test/test_sortb.c
@@ -0,0 +1,87 @@
+/*
+  This file is part of the SC Library.
+  The SC Library provides support for parallel scientific applications.
+
+  Copyright (C) 2010 The University of Texas System
+
+  The SC Library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  The SC Library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with the SC Library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include <sc_sort.h>
+
+const size_t        size = 24;
+
+int
+the_compare (const void *v1, const void *v2)
+{
+  return memcmp (v1, v2, size);
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 rank, num_procs;
+  size_t              nmemb[3], lsize, total;
+  size_t              zz;
+  char               *ldata;
+  sc_MPI_Comm         mpicomm;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+
+  if (num_procs != 3) {
+    SC_GLOBAL_PRODUCTION ("This test will test things only for np = 3\n");
+    goto donothing;
+  }
+
+  nmemb[0] = 7239;
+  nmemb[1] = 7240;
+  nmemb[2] = 7240;
+  lsize = nmemb[rank];
+  total = nmemb[0] + nmemb[1] + nmemb[2];
+
+  srand (17 + (int) total);
+  ldata = SC_ALLOC (char, lsize * size);
+  for (zz = 0; zz < lsize * size; ++zz) {
+    ldata[zz] = (char) (-50. + (300. * rand () / (RAND_MAX + 1.0)));
+  }
+  sc_psort (mpicomm, ldata, nmemb, size, the_compare);
+  /* qsort (ldata, nmemb[rank], size, the_compare); */
+
+  for (zz = 1; zz < lsize; ++zz) {
+    SC_CHECK_ABORT (the_compare (ldata + size * (zz - 1),
+                                 ldata + size * zz) <= 0, "Sort");
+  }
+
+  /* clean up and exit */
+  SC_FREE (ldata);
+
+donothing:
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..ca06c11
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,70 @@
+
+# This file is part of p4est.
+# Makefile.am in src
+# included non-recursively from toplevel directory
+
+libp4est_generated_headers = src/p4est_config.h
+libp4est_installed_headers = src/p4est_base.h
+libp4est_internal_headers =
+libp4est_compiled_sources = src/p4est_base.c
+if P4EST_ENABLE_BUILD_2D
+libp4est_installed_headers += \
+        src/p4est_connectivity.h src/p4est.h src/p4est_extended.h \
+        src/p4est_bits.h src/p4est_search.h \
+        src/p4est_algorithms.h src/p4est_communication.h \
+        src/p4est_ghost.h src/p4est_nodes.h src/p4est_vtk.h \
+        src/p4est_points.h src/p4est_geometry.h \
+        src/p4est_iterate.h src/p4est_lnodes.h src/p4est_mesh.h \
+        src/p4est_balance.h src/p4est_io.h \
+        src/p4est_wrap.h src/p4est_plex.h
+libp4est_compiled_sources += \
+        src/p4est_connectivity.c src/p4est.c \
+        src/p4est_bits.c src/p4est_search.c \
+        src/p4est_algorithms.c src/p4est_communication.c \
+        src/p4est_ghost.c src/p4est_nodes.c src/p4est_vtk.c \
+        src/p4est_points.c src/p4est_geometry.c \
+        src/p4est_iterate.c src/p4est_lnodes.c src/p4est_mesh.c \
+        src/p4est_balance.c src/p4est_io.c \
+        src/p4est_wrap.c src/p4est_plex.c
+endif
+if P4EST_ENABLE_BUILD_3D
+libp4est_installed_headers += \
+        src/p4est_to_p8est.h \
+        src/p8est_connectivity.h src/p8est.h src/p8est_extended.h \
+        src/p8est_bits.h src/p8est_search.h \
+        src/p8est_algorithms.h src/p8est_communication.h \
+        src/p8est_ghost.h src/p8est_nodes.h src/p8est_vtk.h \
+        src/p8est_points.h src/p8est_geometry.h \
+        src/p8est_iterate.h src/p8est_lnodes.h src/p8est_mesh.h \
+        src/p8est_tets_hexes.h src/p8est_balance.h src/p8est_io.h \
+        src/p8est_wrap.h src/p8est_plex.h
+libp4est_compiled_sources += \
+        src/p8est_connectivity.c src/p8est.c \
+        src/p8est_bits.c src/p8est_search.c \
+        src/p8est_algorithms.c src/p8est_communication.c \
+        src/p8est_ghost.c src/p8est_nodes.c src/p8est_vtk.c \
+        src/p8est_points.c src/p8est_geometry.c \
+        src/p8est_iterate.c src/p8est_lnodes.c src/p8est_mesh.c \
+        src/p8est_tets_hexes.c src/p8est_balance.c src/p8est_io.c \
+        src/p8est_wrap.c src/p8est_plex.c
+endif
+include example/p6est/Makefile.am
+
+# this variable is used for headers that are not publicly installed
+P4EST_CPPFLAGS =
+
+lib_LTLIBRARIES += src/libp4est.la
+src_libp4est_la_SOURCES = \
+        $(libp4est_internal_headers) \
+        $(libp4est_compiled_sources)
+src_libp4est_la_CPPFLAGS = $(AM_CPPFLAGS) $(P4EST_CPPFLAGS)
+src_libp4est_la_LDFLAGS = -release $(VERSION)
+LDADD += @top_builddir@/src/libp4est.la @P4EST_SC_LDADD@
+EXTRA_src_libp4est_la_DEPENDENCIES = @P4EST_SC_LDADD@
+
+nodist_include_HEADERS += $(libp4est_generated_headers)
+include_HEADERS += $(libp4est_installed_headers)
+
+AM_CPPFLAGS += -I at top_srcdir@/src @P4EST_SC_CPPFLAGS@
+
+LINT_CSOURCES += $(libp4est_compiled_sources)
diff --git a/src/p4est.c b/src/p4est.c
new file mode 100644
index 0000000..76eebf6
--- /dev/null
+++ b/src/p4est.c
@@ -0,0 +1,3632 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef P4_TO_P8
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_communication.h>
+#include <p8est_extended.h>
+#include <p8est_ghost.h>
+#include <p8est_io.h>
+#else
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_communication.h>
+#include <p4est_extended.h>
+#include <p4est_ghost.h>
+#include <p4est_io.h>
+#endif /* !P4_TO_P8 */
+#include <sc_io.h>
+#include <sc_notify.h>
+#include <sc_ranges.h>
+#include <sc_search.h>
+#ifdef P4EST_HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+#ifdef P4EST_ENABLE_MPIIO
+#define P4EST_MPIIO_WRITE
+#endif
+
+#ifdef P4EST_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+typedef struct
+{
+  int8_t              have_first_count, have_first_load;
+  int8_t              have_second_count, have_second_load;
+  int                 recv_first_count, recv_second_count;
+  int                 send_first_count, send_second_count;
+  sc_array_t          send_first, send_second, recv_first, recv_second;
+}
+p4est_balance_peer_t;
+
+#define p4est_num_ranges (25)
+
+#ifndef P4_TO_P8
+
+static int          p4est_uninitialized_key;
+void               *P4EST_DATA_UNINITIALIZED = &p4est_uninitialized_key;
+
+#endif /* P4_TO_P8 */
+
+static const size_t number_toread_quadrants = 32;
+static const int8_t fully_owned_flag = 0x01;
+static const int8_t any_face_flag = 0x02;
+
+void
+p4est_qcoord_to_vertex (p4est_connectivity_t * connectivity,
+                        p4est_topidx_t treeid,
+                        p4est_qcoord_t x, p4est_qcoord_t y,
+#ifdef P4_TO_P8
+                        p4est_qcoord_t z,
+#endif
+                        double vxyz[3])
+{
+  const double       *vertices = connectivity->vertices;
+#ifdef P4EST_ENABLE_DEBUG
+  const p4est_topidx_t num_vertices = connectivity->num_vertices;
+#endif
+  const p4est_topidx_t *vindices;
+  int                 xi, yi;
+  double              wx[2], wy[2];
+#ifdef P4_TO_P8
+  int                 zi;
+  double              wz[2];
+#endif
+  double              xfactor, yfactor;
+  p4est_topidx_t      vindex;
+
+  P4EST_ASSERT (num_vertices > 0);
+  P4EST_ASSERT (vertices != NULL);
+  P4EST_ASSERT (treeid >= 0 && treeid < connectivity->num_trees);
+
+  P4EST_ASSERT (connectivity->tree_to_vertex != NULL);
+  vindices = connectivity->tree_to_vertex + P4EST_CHILDREN * treeid;
+
+  vxyz[0] = vxyz[1] = vxyz[2] = 0.;
+
+  P4EST_ASSERT (x >= 0 && x <= P4EST_ROOT_LEN);
+  wx[1] = (double) x / (double) P4EST_ROOT_LEN;
+  wx[0] = 1. - wx[1];
+
+  P4EST_ASSERT (y >= 0 && y <= P4EST_ROOT_LEN);
+  wy[1] = (double) y / (double) P4EST_ROOT_LEN;
+  wy[0] = 1. - wy[1];
+
+#ifdef P4_TO_P8
+  P4EST_ASSERT (z >= 0 && z <= P4EST_ROOT_LEN);
+  wz[1] = (double) z / (double) P4EST_ROOT_LEN;
+  wz[0] = 1. - wz[1];
+
+  for (zi = 0; zi < 2; ++zi) {
+#endif
+    for (yi = 0; yi < 2; ++yi) {
+#ifdef P4_TO_P8
+      yfactor = wz[zi] * wy[yi];
+#else
+      yfactor = wy[yi];
+#endif
+      for (xi = 0; xi < 2; ++xi) {
+        xfactor = yfactor * wx[xi];
+
+        vindex = *vindices++;
+        P4EST_ASSERT (vindex >= 0 && vindex < num_vertices);
+
+        vxyz[0] += xfactor * vertices[3 * vindex + 0];
+        vxyz[1] += xfactor * vertices[3 * vindex + 1];
+        vxyz[2] += xfactor * vertices[3 * vindex + 2];
+      }
+    }
+#ifdef P4_TO_P8
+  }
+#endif
+}
+
+size_t
+p4est_memory_used (p4est_t * p4est)
+{
+  const int           mpisize = p4est->mpisize;
+  size_t              size;
+  p4est_topidx_t      nt;
+  p4est_tree_t       *tree;
+
+  size = sizeof (p4est_t) +
+    (mpisize + 1) * (sizeof (p4est_gloidx_t) + sizeof (p4est_quadrant_t));
+
+  size += sc_array_memory_used (p4est->trees, 1);
+  for (nt = 0; nt < p4est->connectivity->num_trees; ++nt) {
+    tree = p4est_tree_array_index (p4est->trees, nt);
+    size += sc_array_memory_used (&tree->quadrants, 0);
+  }
+
+  if (p4est->data_size > 0) {
+    size += sc_mempool_memory_used (p4est->user_data_pool);
+  }
+  size += sc_mempool_memory_used (p4est->quadrant_pool);
+
+  return size;
+}
+
+p4est_t            *
+p4est_new (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity,
+           size_t data_size, p4est_init_t init_fn, void *user_pointer)
+{
+  return p4est_new_ext (mpicomm, connectivity, 0, 0, 1,
+                        data_size, init_fn, user_pointer);
+}
+
+p4est_t            *
+p4est_new_ext (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity,
+               p4est_locidx_t min_quadrants, int min_level, int fill_uniform,
+               size_t data_size, p4est_init_t init_fn, void *user_pointer)
+{
+  int                 mpiret;
+  int                 num_procs, rank;
+  int                 i, must_remove_last_quadrant;
+  int                 level;
+  uint64_t            first_morton, last_morton, miu, count;
+  p4est_topidx_t      jt, num_trees;
+  p4est_gloidx_t      tree_num_quadrants, global_num_quadrants;
+  p4est_gloidx_t      first_tree, first_quadrant, first_tree_quadrant;
+  p4est_gloidx_t      last_tree, last_quadrant, last_tree_quadrant;
+  p4est_gloidx_t      quadrant_index;
+  p4est_t            *p4est;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *quad;
+  p4est_quadrant_t    a, b, c;
+  p4est_quadrant_t   *global_first_position;
+  sc_array_t         *tquadrants;
+
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Into " P4EST_STRING
+     "_new with min quadrants %lld level %d uniform %d\n",
+     (long long) min_quadrants, SC_MAX (min_level, 0), fill_uniform);
+  p4est_log_indent_push ();
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (connectivity));
+  P4EST_ASSERT (min_level <= P4EST_QMAXLEVEL);
+
+  /* retrieve MPI information */
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  /* assign some data members */
+  p4est = P4EST_ALLOC_ZERO (p4est_t, 1);
+  p4est->mpicomm = mpicomm;
+  p4est->mpisize = num_procs;
+  p4est->mpirank = rank;
+  p4est->data_size = data_size;
+  p4est->user_pointer = user_pointer;
+  p4est->connectivity = connectivity;
+  num_trees = connectivity->num_trees;
+
+  /* allocate memory pools */
+  if (p4est->data_size > 0) {
+    p4est->user_data_pool = sc_mempool_new (p4est->data_size);
+  }
+  else {
+    p4est->user_data_pool = NULL;
+  }
+  p4est->quadrant_pool = sc_mempool_new (sizeof (p4est_quadrant_t));
+
+  /* determine uniform level of initial tree */
+  tree_num_quadrants = 1;
+  for (level = 0; level < P4EST_QMAXLEVEL; ++level) {
+    if (tree_num_quadrants >=
+        (num_procs * (p4est_gloidx_t) min_quadrants + (num_trees - 1))
+        / num_trees) {
+      break;
+    }
+    tree_num_quadrants *= P4EST_CHILDREN;
+    P4EST_ASSERT (tree_num_quadrants > 0);
+  }
+  for (; level < min_level; ++level) {
+    tree_num_quadrants *= P4EST_CHILDREN;
+    P4EST_ASSERT (tree_num_quadrants > 0);
+  }
+  P4EST_ASSERT (level <= P4EST_QMAXLEVEL
+                && tree_num_quadrants <= (p4est_gloidx_t) P4EST_LOCIDX_MAX);
+
+  /* compute global number of quadrants */
+  global_num_quadrants = tree_num_quadrants * num_trees;
+  P4EST_GLOBAL_PRODUCTIONF ("New " P4EST_STRING
+                            " with %lld trees on %d processors\n",
+                            (long long) num_trees, num_procs);
+  P4EST_GLOBAL_INFOF ("Initial level %d potential global quadrants"
+                      " %lld per tree %lld\n",
+                      level, (long long) global_num_quadrants,
+                      (long long) tree_num_quadrants);
+
+  /* compute index of first tree for this processor */
+  first_quadrant =
+    p4est_partition_cut_gloidx (global_num_quadrants, rank, num_procs);
+  first_tree = first_quadrant / tree_num_quadrants;
+  first_tree_quadrant = first_quadrant - first_tree * tree_num_quadrants;
+  last_quadrant = p4est_partition_cut_gloidx (global_num_quadrants, rank + 1,
+                                              num_procs) - 1;
+  P4EST_VERBOSEF
+    ("first tree %lld first quadrant %lld global quadrant %lld\n",
+     (long long) first_tree, (long long) first_tree_quadrant,
+     (long long) first_quadrant);
+  P4EST_ASSERT (first_tree_quadrant < tree_num_quadrants);
+
+  /* compute index of last tree for this processor */
+  if (first_quadrant <= last_quadrant) {
+    last_tree = last_quadrant / tree_num_quadrants;
+    last_tree_quadrant = last_quadrant - last_tree * tree_num_quadrants;
+    P4EST_VERBOSEF
+      ("last tree %lld last quadrant %lld global quadrant %lld\n",
+       (long long) last_tree, (long long) last_tree_quadrant,
+       (long long) last_quadrant);
+
+    /* check ranges of various integers to be 32bit compatible */
+    P4EST_ASSERT (first_tree <= last_tree && last_tree < num_trees);
+    P4EST_ASSERT (0 <= first_tree_quadrant && 0 <= last_tree_quadrant);
+    P4EST_ASSERT (last_tree_quadrant < tree_num_quadrants);
+    if (first_tree == last_tree) {
+      P4EST_ASSERT (first_tree_quadrant <= last_tree_quadrant);
+    }
+  }
+  else {
+    P4EST_VERBOSE ("Empty processor");
+    P4EST_ASSERT (0 <= first_tree && 0 <= first_tree_quadrant);
+    first_tree = -1;
+    last_tree = -2;
+    last_tree_quadrant = -1;
+  }
+
+  /* allocate trees and quadrants */
+  p4est->trees = sc_array_new (sizeof (p4est_tree_t));
+  sc_array_resize (p4est->trees, num_trees);
+  for (jt = 0; jt < num_trees; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    sc_array_init (&tree->quadrants, sizeof (p4est_quadrant_t));
+    P4EST_QUADRANT_INIT (&tree->first_desc);
+    P4EST_QUADRANT_INIT (&tree->last_desc);
+    tree->quadrants_offset = 0;
+    for (i = 0; i <= P4EST_QMAXLEVEL; ++i) {
+      tree->quadrants_per_level[i] = 0;
+    }
+    for (; i <= P4EST_MAXLEVEL; ++i) {
+      tree->quadrants_per_level[i] = -1;
+    }
+    tree->maxlevel = 0;
+  }
+  p4est->local_num_quadrants = 0;
+  p4est->global_num_quadrants = 0;
+
+  /* for every locally non-empty tree fill first and last quadrant */
+  P4EST_QUADRANT_INIT (&a);
+  P4EST_QUADRANT_INIT (&b);
+  P4EST_QUADRANT_INIT (&c);
+  for (jt = first_tree; jt <= last_tree; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    tquadrants = &tree->quadrants;
+
+    quad = NULL;
+    if (!fill_uniform) {        /* fill with coarsest possible quadrants */
+      must_remove_last_quadrant = 0;
+
+      /* set morton id of first quadrant and initialize user data */
+      if (jt == first_tree) {
+        p4est_quadrant_set_morton (&a, level, first_tree_quadrant);
+      }
+      else {
+        p4est_quadrant_set_morton (&a, level, 0);
+      }
+#ifdef P4_TO_P8
+      P4EST_LDEBUGF ("tree %lld first morton 0x%llx 0x%llx 0x%llx\n",
+                     (long long) jt, (long long) a.x,
+                     (long long) a.y, (long long) a.z);
+#else
+      P4EST_LDEBUGF ("tree %lld first morton 0x%llx 0x%llx\n",
+                     (long long) jt, (long long) a.x, (long long) a.y);
+#endif
+      p4est_quadrant_first_descendant (&a, &tree->first_desc,
+                                       P4EST_QMAXLEVEL);
+
+      /* set morton id of last quadrant */
+      if (tree_num_quadrants == 1 ||
+          (jt == first_tree
+           && first_tree_quadrant == tree_num_quadrants - 1)) {
+        /* There is only a in the tree */
+        quad = p4est_quadrant_array_push (tquadrants);
+        *quad = a;
+        p4est_quadrant_init_data (p4est, jt, quad, init_fn);
+        tree->maxlevel = a.level;
+        tree->quadrants_per_level[a.level] = 1;
+      }
+      else {
+        if (jt == last_tree) {
+          if (last_tree_quadrant == tree_num_quadrants - 1) {
+            quadrant_index = last_tree_quadrant;
+          }
+          else {
+            quadrant_index = last_tree_quadrant + 1;
+            must_remove_last_quadrant = 1;
+          }
+          p4est_quadrant_set_morton (&b, level, quadrant_index);
+        }
+        else {
+          p4est_quadrant_set_morton (&b, level, tree_num_quadrants - 1);
+        }
+#ifdef P4_TO_P8
+        P4EST_LDEBUGF ("tree %lld last morton 0x%llx 0x%llx 0x%llx\n",
+                       (long long) jt, (long long) b.x,
+                       (long long) b.y, (long long) b.z);
+#else
+        P4EST_LDEBUGF ("tree %lld last morton 0x%llx 0x%llx\n",
+                       (long long) jt, (long long) b.x, (long long) b.y);
+#endif
+        /* fill up tree between a and b with coarse quadrants */
+        p4est_complete_region (p4est, &a, 1, &b, !must_remove_last_quadrant,
+                               tree, jt, init_fn);
+        quad = p4est_quadrant_array_index (tquadrants,
+                                           tquadrants->elem_count - 1);
+      }
+    }
+    else {                      /* fill tree with quadrants of given level */
+      /* determine range of quadrants in this tree */
+      first_morton = (uint64_t)
+        (jt == first_tree ? first_tree_quadrant : 0);
+      last_morton = (uint64_t)
+        (jt == last_tree ? last_tree_quadrant : tree_num_quadrants - 1);
+      count = last_morton - first_morton + 1;
+      P4EST_ASSERT (count > 0);
+
+      /* populate quadrant array in Morton order */
+      sc_array_resize (tquadrants, (size_t) count);
+      for (miu = 0; miu < count; ++miu) {
+        quad = p4est_quadrant_array_index (tquadrants, (size_t) miu);
+        p4est_quadrant_set_morton (quad, level, first_morton + miu);
+        p4est_quadrant_init_data (p4est, jt, quad, init_fn);
+      }
+
+      /* remember first tree position */
+      p4est_quadrant_first_descendant (p4est_quadrant_array_index
+                                       (tquadrants, 0), &tree->first_desc,
+                                       P4EST_QMAXLEVEL);
+
+      /* set tree counters */
+      tree->maxlevel = (int8_t) level;
+      tree->quadrants_per_level[level] = (p4est_locidx_t) count;
+    }
+
+#if 0
+    P4EST_VERBOSEF ("tree %lld quadrants %llu\n", (long long) jt,
+                    (unsigned long long) tquadrants->elem_count);
+#endif
+
+    tree->quadrants_offset = p4est->local_num_quadrants;
+    p4est->local_num_quadrants += tquadrants->elem_count;
+    p4est_quadrant_last_descendant (quad, &tree->last_desc, P4EST_QMAXLEVEL);
+  }
+  if (last_tree >= 0) {
+    for (; jt < num_trees; ++jt) {
+      tree = p4est_tree_array_index (p4est->trees, jt);
+      tree->quadrants_offset = p4est->local_num_quadrants;
+    }
+  }
+
+  /* compute some member variables */
+  p4est->first_local_tree = first_tree;
+  p4est->last_local_tree = last_tree;
+  p4est->global_first_quadrant = P4EST_ALLOC (p4est_gloidx_t, num_procs + 1);
+  if (!fill_uniform && level > 0) {
+    /* this performs an allgather to count all quadrants */
+    p4est_comm_count_quadrants (p4est);
+  }
+  else {
+    /* for a uniform forest we know all global information a priori */
+    for (i = 0; i <= num_procs; ++i) {
+      p4est->global_first_quadrant[i] =
+        p4est_partition_cut_gloidx (global_num_quadrants, i, num_procs);
+    }
+    p4est->global_num_quadrants = global_num_quadrants;
+  }
+
+  /* fill in global partition information */
+  global_first_position = P4EST_ALLOC_ZERO (p4est_quadrant_t, num_procs + 1);
+  for (i = 0; i <= num_procs; ++i) {
+    first_quadrant =
+      p4est_partition_cut_gloidx (global_num_quadrants, i, num_procs);
+    first_tree = first_quadrant / tree_num_quadrants;
+    first_tree_quadrant = first_quadrant - first_tree * tree_num_quadrants;
+    p4est_quadrant_set_morton (&c, level, first_tree_quadrant);
+    global_first_position[i].x = c.x;
+    global_first_position[i].y = c.y;
+#ifdef P4_TO_P8
+    global_first_position[i].z = c.z;
+#endif
+    global_first_position[i].level = P4EST_QMAXLEVEL;
+    global_first_position[i].p.which_tree = first_tree;
+  }
+  p4est->global_first_position = global_first_position;
+
+  /* print more statistics */
+  P4EST_VERBOSEF ("total local quadrants %lld\n",
+                  (long long) p4est->local_num_quadrants);
+
+  P4EST_ASSERT (p4est_is_valid (p4est));
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done " P4EST_STRING
+                            "_new with %lld total quadrants\n",
+                            (long long) p4est->global_num_quadrants);
+  return p4est;
+}
+
+void
+p4est_destroy (p4est_t * p4est)
+{
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              qz;
+#endif
+  p4est_topidx_t      jt;
+  p4est_tree_t       *tree;
+
+  for (jt = 0; jt < p4est->connectivity->num_trees; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+
+#ifdef P4EST_ENABLE_DEBUG
+    for (qz = 0; qz < tree->quadrants.elem_count; ++qz) {
+      p4est_quadrant_t   *quad =
+        p4est_quadrant_array_index (&tree->quadrants, qz);
+      p4est_quadrant_free_data (p4est, quad);
+    }
+#endif
+
+    sc_array_reset (&tree->quadrants);
+  }
+  sc_array_destroy (p4est->trees);
+
+  if (p4est->user_data_pool != NULL) {
+    sc_mempool_destroy (p4est->user_data_pool);
+  }
+  sc_mempool_destroy (p4est->quadrant_pool);
+
+  P4EST_FREE (p4est->global_first_quadrant);
+  P4EST_FREE (p4est->global_first_position);
+  P4EST_FREE (p4est);
+}
+
+p4est_t            *
+p4est_copy (p4est_t * input, int copy_data)
+{
+  const p4est_topidx_t num_trees = input->connectivity->num_trees;
+  const p4est_topidx_t first_tree = input->first_local_tree;
+  const p4est_topidx_t last_tree = input->last_local_tree;
+  size_t              icount;
+  size_t              zz;
+  p4est_topidx_t      jt;
+  p4est_t            *p4est;
+  p4est_tree_t       *itree, *ptree;
+  p4est_quadrant_t   *iq, *pq;
+  sc_array_t         *iquadrants, *pquadrants;
+
+  /* create a shallow copy and zero out dependent fields */
+  p4est = P4EST_ALLOC (p4est_t, 1);
+  memcpy (p4est, input, sizeof (p4est_t));
+  p4est->global_first_quadrant = NULL;
+  p4est->global_first_position = NULL;
+  p4est->trees = NULL;
+  p4est->user_data_pool = NULL;
+  p4est->quadrant_pool = NULL;
+
+  /* allocate a user data pool if necessary and a quadrant pool */
+  if (copy_data && p4est->data_size > 0) {
+    p4est->user_data_pool = sc_mempool_new (p4est->data_size);
+  }
+  else {
+    p4est->data_size = 0;
+  }
+  p4est->quadrant_pool = sc_mempool_new (sizeof (p4est_quadrant_t));
+
+  /* copy quadrants for each tree */
+  p4est->trees = sc_array_new (sizeof (p4est_tree_t));
+  sc_array_resize (p4est->trees, num_trees);
+  for (jt = 0; jt < num_trees; ++jt) {
+    itree = p4est_tree_array_index (input->trees, jt);
+    ptree = p4est_tree_array_index (p4est->trees, jt);
+    memcpy (ptree, itree, sizeof (p4est_tree_t));
+    sc_array_init (&ptree->quadrants, sizeof (p4est_quadrant_t));
+  }
+  for (jt = first_tree; jt <= last_tree; ++jt) {
+    itree = p4est_tree_array_index (input->trees, jt);
+    iquadrants = &itree->quadrants;
+    icount = iquadrants->elem_count;
+    ptree = p4est_tree_array_index (p4est->trees, jt);
+    pquadrants = &ptree->quadrants;
+    sc_array_resize (pquadrants, icount);
+    memcpy (pquadrants->array, iquadrants->array,
+            icount * sizeof (p4est_quadrant_t));
+    if (p4est->data_size > 0) {
+      P4EST_ASSERT (copy_data);
+      for (zz = 0; zz < icount; ++zz) {
+        iq = p4est_quadrant_array_index (iquadrants, zz);
+        pq = p4est_quadrant_array_index (pquadrants, zz);
+        pq->p.user_data = sc_mempool_alloc (p4est->user_data_pool);
+        memcpy (pq->p.user_data, iq->p.user_data, p4est->data_size);
+      }
+    }
+  }
+
+  /* allocate and copy global quadrant count */
+  p4est->global_first_quadrant =
+    P4EST_ALLOC (p4est_gloidx_t, p4est->mpisize + 1);
+  memcpy (p4est->global_first_quadrant, input->global_first_quadrant,
+          (p4est->mpisize + 1) * sizeof (p4est_gloidx_t));
+
+  /* allocate and copy global partition information */
+  p4est->global_first_position = P4EST_ALLOC (p4est_quadrant_t,
+                                              p4est->mpisize + 1);
+  memcpy (p4est->global_first_position, input->global_first_position,
+          (p4est->mpisize + 1) * sizeof (p4est_quadrant_t));
+
+  /* check for valid p4est and return */
+  P4EST_ASSERT (p4est_is_valid (p4est));
+
+  return p4est;
+}
+
+void
+p4est_reset_data (p4est_t * p4est, size_t data_size,
+                  p4est_init_t init_fn, void *user_pointer)
+{
+  int                 doresize;
+  size_t              zz;
+  p4est_topidx_t      jt;
+  p4est_quadrant_t   *q;
+  p4est_tree_t       *tree;
+  sc_array_t         *tquadrants;
+
+  doresize = (p4est->data_size != data_size);
+
+  p4est->data_size = data_size;
+  p4est->user_pointer = user_pointer;
+
+  if (doresize) {
+    if (p4est->user_data_pool != NULL) {
+      sc_mempool_destroy (p4est->user_data_pool);
+    }
+    if (p4est->data_size > 0) {
+      p4est->user_data_pool = sc_mempool_new (p4est->data_size);
+    }
+    else {
+      p4est->user_data_pool = NULL;
+    }
+  }
+
+  for (jt = p4est->first_local_tree; jt <= p4est->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    tquadrants = &tree->quadrants;
+    for (zz = 0; zz < tquadrants->elem_count; ++zz) {
+      q = p4est_quadrant_array_index (tquadrants, zz);
+      if (doresize) {
+        if (p4est->data_size > 0) {
+          q->p.user_data = sc_mempool_alloc (p4est->user_data_pool);
+        }
+        else {
+          q->p.user_data = NULL;
+        }
+      }
+      if (init_fn != NULL) {
+        init_fn (p4est, jt, q);
+      }
+    }
+  }
+}
+
+void
+p4est_refine (p4est_t * p4est, int refine_recursive,
+              p4est_refine_t refine_fn, p4est_init_t init_fn)
+{
+  p4est_refine_ext (p4est, refine_recursive, -1, refine_fn, init_fn, NULL);
+}
+
+void
+p4est_refine_ext (p4est_t * p4est, int refine_recursive, int allowed_level,
+                  p4est_refine_t refine_fn, p4est_init_t init_fn,
+                  p4est_replace_t replace_fn)
+{
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              quadrant_pool_size, data_pool_size;
+#endif
+  int                 firsttime;
+  int                 i, maxlevel;
+  p4est_topidx_t      nt;
+  size_t              incount, current, restpos, movecount;
+  sc_list_t          *list;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *q, *qalloc, *qpop;
+  p4est_quadrant_t   *c0, *c1, *c2, *c3;
+#ifdef P4_TO_P8
+  p4est_quadrant_t   *c4, *c5, *c6, *c7;
+#endif
+  sc_array_t         *tquadrants;
+  p4est_quadrant_t   *family[8];
+  p4est_quadrant_t    parent, *pp = &parent;
+
+  if (allowed_level < 0) {
+    allowed_level = P4EST_QMAXLEVEL;
+  }
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING
+                            "_refine with %lld total quadrants,"
+                            " allowed level %d\n",
+                            (long long) p4est->global_num_quadrants,
+                            allowed_level);
+  p4est_log_indent_push ();
+  P4EST_ASSERT (p4est_is_valid (p4est));
+  P4EST_ASSERT (0 <= allowed_level && allowed_level <= P4EST_QMAXLEVEL);
+  P4EST_ASSERT (refine_fn != NULL);
+
+  /*
+     q points to a quadrant that is an array member
+     qalloc is a quadrant that has been allocated through quadrant_pool
+     qpop is a quadrant that has been allocated through quadrant_pool
+     never mix these two types of quadrant pointers
+
+     The quadrant->pad8 field of list quadrants is interpreted as boolean
+     and set to true for quadrants that have already been refined.
+   */
+  list = sc_list_new (NULL);
+  p4est->local_num_quadrants = 0;
+
+  /* loop over all local trees */
+  for (nt = p4est->first_local_tree; nt <= p4est->last_local_tree; ++nt) {
+    tree = p4est_tree_array_index (p4est->trees, nt);
+    tree->quadrants_offset = p4est->local_num_quadrants;
+    tquadrants = &tree->quadrants;
+#ifdef P4EST_ENABLE_DEBUG
+    quadrant_pool_size = p4est->quadrant_pool->elem_count;
+    data_pool_size = 0;
+    if (p4est->user_data_pool != NULL) {
+      data_pool_size = p4est->user_data_pool->elem_count;
+    }
+#endif
+
+    /* initial log message for this tree */
+    P4EST_VERBOSEF ("Into refine tree %lld with %llu\n", (long long) nt,
+                    (unsigned long long) tquadrants->elem_count);
+
+    /* reset the quadrant counters */
+    maxlevel = 0;
+    for (i = 0; i <= P4EST_QMAXLEVEL; ++i) {
+      tree->quadrants_per_level[i] = 0;
+    }
+
+    /* run through the array to find first quadrant to be refined */
+    q = NULL;
+    incount = tquadrants->elem_count;
+    for (current = 0; current < incount; ++current) {
+      q = p4est_quadrant_array_index (tquadrants, current);
+      if (refine_fn (p4est, nt, q) && (int) q->level < allowed_level) {
+        break;
+      }
+      maxlevel = SC_MAX (maxlevel, (int) q->level);
+      ++tree->quadrants_per_level[q->level];
+    }
+    if (current == incount) {
+      /* no refinement occurs in this tree */
+      p4est->local_num_quadrants += incount;
+      continue;
+    }
+    P4EST_ASSERT (q != NULL);
+
+    /* now we have a quadrant to refine, prepend it to the list */
+    qalloc = p4est_quadrant_mempool_alloc (p4est->quadrant_pool);
+    *qalloc = *q;               /* never prepend array members directly */
+    qalloc->pad8 = 0;           /* this quadrant has not been refined yet */
+    sc_list_prepend (list, qalloc);     /* only newly allocated quadrants */
+
+    P4EST_QUADRANT_INIT (&parent);
+
+    /*
+       current points to the next array member to write
+       restpos points to the next array member to read
+     */
+    restpos = current + 1;
+
+    /* run through the list and refine recursively */
+    firsttime = 1;
+    while (list->elem_count > 0) {
+      qpop = p4est_quadrant_list_pop (list);
+      if (firsttime ||
+          ((refine_recursive || !qpop->pad8) &&
+           refine_fn (p4est, nt, qpop) &&
+           (int) qpop->level < allowed_level)) {
+        firsttime = 0;
+        sc_array_resize (tquadrants,
+                         tquadrants->elem_count + P4EST_CHILDREN - 1);
+
+        if (replace_fn != NULL) {
+          /* do not free qpop's data yet: we will do this when the parent
+           * is replaced */
+          parent = *qpop;
+        }
+        else {
+          p4est_quadrant_free_data (p4est, qpop);
+        }
+        c0 = qpop;
+        c1 = p4est_quadrant_mempool_alloc (p4est->quadrant_pool);
+        c2 = p4est_quadrant_mempool_alloc (p4est->quadrant_pool);
+        c3 = p4est_quadrant_mempool_alloc (p4est->quadrant_pool);
+
+#ifdef P4_TO_P8
+        c4 = p4est_quadrant_mempool_alloc (p4est->quadrant_pool);
+        c5 = p4est_quadrant_mempool_alloc (p4est->quadrant_pool);
+        c6 = p4est_quadrant_mempool_alloc (p4est->quadrant_pool);
+        c7 = p4est_quadrant_mempool_alloc (p4est->quadrant_pool);
+
+        p8est_quadrant_children (qpop, c0, c1, c2, c3, c4, c5, c6, c7);
+#else
+        p4est_quadrant_children (qpop, c0, c1, c2, c3);
+#endif
+        p4est_quadrant_init_data (p4est, nt, c0, init_fn);
+        p4est_quadrant_init_data (p4est, nt, c1, init_fn);
+        p4est_quadrant_init_data (p4est, nt, c2, init_fn);
+        p4est_quadrant_init_data (p4est, nt, c3, init_fn);
+        c0->pad8 = c1->pad8 = c2->pad8 = c3->pad8 = 1;
+
+#ifdef P4_TO_P8
+        p4est_quadrant_init_data (p4est, nt, c4, init_fn);
+        p4est_quadrant_init_data (p4est, nt, c5, init_fn);
+        p4est_quadrant_init_data (p4est, nt, c6, init_fn);
+        p4est_quadrant_init_data (p4est, nt, c7, init_fn);
+        c4->pad8 = c5->pad8 = c6->pad8 = c7->pad8 = 1;
+
+        sc_list_prepend (list, c7);
+        sc_list_prepend (list, c6);
+        sc_list_prepend (list, c5);
+        sc_list_prepend (list, c4);
+#endif
+        sc_list_prepend (list, c3);
+        sc_list_prepend (list, c2);
+        sc_list_prepend (list, c1);
+        sc_list_prepend (list, c0);
+
+        if (replace_fn != NULL) {
+          /* in family mode we always call the replace callback right
+           * away */
+          family[0] = c0;
+          family[1] = c1;
+          family[2] = c2;
+          family[3] = c3;
+#ifdef P4_TO_P8
+          family[4] = c4;
+          family[5] = c5;
+          family[6] = c6;
+          family[7] = c7;
+#endif
+          replace_fn (p4est, nt, 1, &pp, P4EST_CHILDREN, family);
+          p4est_quadrant_free_data (p4est, &parent);
+        }
+      }
+      else {
+        /* need to make room in the array to store this new quadrant */
+        if (restpos < incount && current == restpos) {
+          movecount = SC_MIN (incount - restpos, number_toread_quadrants);
+          while (movecount > 0) {
+            q = p4est_quadrant_array_index (tquadrants, restpos);
+            qalloc = p4est_quadrant_mempool_alloc (p4est->quadrant_pool);
+            *qalloc = *q;       /* never append array members directly */
+            qalloc->pad8 = 0;   /* has not been refined yet */
+            sc_list_append (list, qalloc);      /* only newly allocated quadrants */
+            --movecount;
+            ++restpos;
+          }
+        }
+
+        /* store new quadrant and update counters */
+        q = p4est_quadrant_array_index (tquadrants, current);
+        *q = *qpop;
+        maxlevel = SC_MAX (maxlevel, (int) qpop->level);
+        ++tree->quadrants_per_level[qpop->level];
+        ++current;
+        sc_mempool_free (p4est->quadrant_pool, qpop);
+      }
+    }
+    tree->maxlevel = (int8_t) maxlevel;
+    p4est->local_num_quadrants += tquadrants->elem_count;
+
+    P4EST_ASSERT (restpos == incount);
+    P4EST_ASSERT (current == tquadrants->elem_count);
+    P4EST_ASSERT (list->first == NULL && list->last == NULL);
+    P4EST_ASSERT (quadrant_pool_size == p4est->quadrant_pool->elem_count);
+    if (p4est->user_data_pool != NULL) {
+      P4EST_ASSERT (data_pool_size + tquadrants->elem_count ==
+                    p4est->user_data_pool->elem_count + incount);
+    }
+    P4EST_ASSERT (p4est_tree_is_sorted (tree));
+    P4EST_ASSERT (p4est_tree_is_complete (tree));
+
+    /* final log message for this tree */
+    P4EST_VERBOSEF ("Done refine tree %lld now %llu\n", (long long) nt,
+                    (unsigned long long) tquadrants->elem_count);
+  }
+  if (p4est->last_local_tree >= 0) {
+    for (; nt < p4est->connectivity->num_trees; ++nt) {
+      tree = p4est_tree_array_index (p4est->trees, nt);
+      tree->quadrants_offset = p4est->local_num_quadrants;
+    }
+  }
+
+  sc_list_destroy (list);
+
+  /* compute global number of quadrants */
+  p4est_comm_count_quadrants (p4est);
+
+  P4EST_ASSERT (p4est_is_valid (p4est));
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done " P4EST_STRING
+                            "_refine with %lld total quadrants\n",
+                            (long long) p4est->global_num_quadrants);
+}
+
+void
+p4est_coarsen (p4est_t * p4est, int coarsen_recursive,
+               p4est_coarsen_t coarsen_fn, p4est_init_t init_fn)
+{
+  p4est_coarsen_ext (p4est, coarsen_recursive, 0, coarsen_fn, init_fn, NULL);
+}
+
+void
+p4est_coarsen_ext (p4est_t * p4est,
+                   int coarsen_recursive, int callback_orphans,
+                   p4est_coarsen_t coarsen_fn, p4est_init_t init_fn,
+                   p4est_replace_t replace_fn)
+{
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              data_pool_size;
+#endif
+  int                 i, maxlevel;
+  int                 isfamily;
+  size_t              zz;
+  size_t              incount, removed;
+  size_t              window, start, length, cidz;
+  p4est_locidx_t      num_quadrants, prev_offset;
+  p4est_topidx_t      jt;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *c[P4EST_CHILDREN];
+  p4est_quadrant_t   *cfirst, *clast;
+  sc_array_t         *tquadrants;
+  p4est_quadrant_t    qtemp;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING
+                            "_coarsen with %lld total quadrants\n",
+                            (long long) p4est->global_num_quadrants);
+  p4est_log_indent_push ();
+  P4EST_ASSERT (p4est_is_valid (p4est));
+  P4EST_ASSERT (coarsen_fn != NULL);
+
+  P4EST_QUADRANT_INIT (&qtemp);
+
+  /* loop over all local trees */
+  prev_offset = 0;
+  for (jt = p4est->first_local_tree; jt <= p4est->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    tquadrants = &tree->quadrants;
+#ifdef P4EST_ENABLE_DEBUG
+    data_pool_size = 0;
+    if (p4est->user_data_pool != NULL) {
+      data_pool_size = p4est->user_data_pool->elem_count;
+    }
+#endif
+    removed = 0;
+
+    /* initial log message for this tree */
+    P4EST_VERBOSEF ("Into coarsen tree %lld with %llu\n", (long long) jt,
+                    (unsigned long long) tquadrants->elem_count);
+
+    /* state information */
+    window = 0;                 /* start position of sliding window in array */
+    start = 1;                  /* start position of hole in window/array */
+    length = 0;                 /* length of hole in window/array */
+
+    /* run through the array and coarsen recursively */
+    incount = tquadrants->elem_count;
+    while (window + P4EST_CHILDREN + length <= incount) {
+      P4EST_ASSERT (window < start);
+
+      cidz = incount;
+      isfamily = 1;
+      for (zz = 0; zz < P4EST_CHILDREN; ++zz) {
+        c[zz] = (window + zz < start) ?
+          p4est_quadrant_array_index (tquadrants, window + zz) :
+          p4est_quadrant_array_index (tquadrants, window + length + zz);
+
+        if (zz != (size_t) p4est_quadrant_child_id (c[zz])) {
+          isfamily = 0;
+          if (callback_orphans) {
+            c[1] = NULL;
+            (void) coarsen_fn (p4est, jt, c);
+          }
+          break;
+        }
+      }
+      /* in a complete tree, the only way P4EST_CHILDREN consecutive quadrants
+       * have the correct consecutive child_id's is if they are, in fact, a
+       * family.
+       */
+      P4EST_ASSERT (!isfamily || p4est_quadrant_is_familypv (c));
+      if (isfamily && coarsen_fn (p4est, jt, c)) {
+        /* coarsen this family of quadrants */
+        if (replace_fn == NULL) {
+          for (zz = 0; zz < P4EST_CHILDREN; ++zz) {
+            p4est_quadrant_free_data (p4est, c[zz]);
+          }
+        }
+        tree->quadrants_per_level[c[0]->level] -= P4EST_CHILDREN;
+        cfirst = c[0];
+        if (replace_fn != NULL) {
+          qtemp = *(c[0]);
+          c[0] = &qtemp;
+        }
+        p4est_quadrant_parent (c[0], cfirst);
+        p4est_quadrant_init_data (p4est, jt, cfirst, init_fn);
+        tree->quadrants_per_level[cfirst->level] += 1;
+        p4est->local_num_quadrants -= P4EST_CHILDREN - 1;
+        removed += P4EST_CHILDREN - 1;
+
+        cidz = (size_t) p4est_quadrant_child_id (cfirst);
+        start = window + 1;
+        length += P4EST_CHILDREN - 1;
+
+        if (replace_fn != NULL) {
+          replace_fn (p4est, jt, P4EST_CHILDREN, c, 1, &cfirst);
+          for (zz = 0; zz < P4EST_CHILDREN; zz++) {
+            p4est_quadrant_free_data (p4est, c[zz]);
+          }
+        }
+      }
+
+      if (cidz <= window && coarsen_recursive) {
+        window -= cidz;
+      }
+      else {
+        ++window;
+        if (window == start && start + length < incount) {
+          if (length > 0) {
+            cfirst = p4est_quadrant_array_index (tquadrants, start);
+            clast = p4est_quadrant_array_index (tquadrants, start + length);
+            *cfirst = *clast;
+          }
+          start = window + 1;
+        }
+      }
+    }
+
+    /* adjust final array size */
+    if (length > 0) {
+      for (zz = start + length; zz < incount; ++zz) {
+        cfirst = p4est_quadrant_array_index (tquadrants, zz - length);
+        clast = p4est_quadrant_array_index (tquadrants, zz);
+        *cfirst = *clast;
+      }
+      sc_array_resize (tquadrants, incount - length);
+    }
+
+    /* call remaining orphans */
+    if (callback_orphans) {
+      c[1] = NULL;
+      for (zz = window; zz < incount - length; ++zz) {
+        c[0] = p4est_quadrant_array_index (tquadrants, zz);
+        (void) coarsen_fn (p4est, jt, c);
+      }
+    }
+
+    /* compute maximum level */
+    maxlevel = 0;
+    num_quadrants = 0;
+    for (i = 0; i <= P4EST_QMAXLEVEL; ++i) {
+      P4EST_ASSERT (tree->quadrants_per_level[i] >= 0);
+      num_quadrants += tree->quadrants_per_level[i];    /* same type */
+      if (tree->quadrants_per_level[i] > 0) {
+        maxlevel = i;
+      }
+    }
+    tree->maxlevel = (int8_t) maxlevel;
+    tree->quadrants_offset = prev_offset;
+    prev_offset += num_quadrants;
+
+    /* do some sanity checks */
+    P4EST_ASSERT (num_quadrants == (p4est_locidx_t) tquadrants->elem_count);
+    P4EST_ASSERT (tquadrants->elem_count == incount - removed);
+    if (p4est->user_data_pool != NULL) {
+      P4EST_ASSERT (data_pool_size - removed ==
+                    p4est->user_data_pool->elem_count);
+    }
+    P4EST_ASSERT (p4est_tree_is_sorted (tree));
+    P4EST_ASSERT (p4est_tree_is_complete (tree));
+
+    /* final log message for this tree */
+    P4EST_VERBOSEF ("Done coarsen tree %lld now %llu\n", (long long) jt,
+                    (unsigned long long) tquadrants->elem_count);
+  }
+  if (p4est->last_local_tree >= 0) {
+    for (; jt < p4est->connectivity->num_trees; ++jt) {
+      tree = p4est_tree_array_index (p4est->trees, jt);
+      tree->quadrants_offset = p4est->local_num_quadrants;
+    }
+  }
+
+  /* compute global number of quadrants */
+  p4est_comm_count_quadrants (p4est);
+
+  P4EST_ASSERT (p4est_is_valid (p4est));
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done " P4EST_STRING
+                            "_coarsen with %lld total quadrants\n",
+                            (long long) p4est->global_num_quadrants);
+}
+
+/** Check if the insulation layer of a quadrant overlaps anybody.
+ * If yes, the quadrant itself is scheduled for sending.
+ * Both quadrants are in the receiving tree's coordinates.
+ * \param [in]  qtree       Tree id of the receiving tree.
+ * \param [in]  inter_tree  Boolean flag to specify inter-tree communication.
+ * \param [in]  q           The quadrant to be sent if there is overlap.
+ * \param [in]  insul       An insulation quadrant of \a q.
+ * \param [in,out]  first_peer  Lowest peer, will be updated.
+ * \param [in,out]  last_peer   Highest peer, will be updated.
+ */
+static void
+p4est_balance_schedule (p4est_t * p4est, p4est_balance_peer_t * peers,
+                        p4est_topidx_t qtree, int inter_tree,
+                        const p4est_quadrant_t * q,
+                        const p4est_quadrant_t * insul,
+                        int *first_peer, int *last_peer)
+{
+  const int           rank = p4est->mpirank;
+  int                 found;
+  int                 back, pos;
+  int                 owner, first_owner, last_owner;
+  p4est_gloidx_t     *global_first_quadrant = p4est->global_first_quadrant;
+  p4est_quadrant_t    ld, *s;
+  p4est_balance_peer_t *peer;
+
+  P4EST_QUADRANT_INIT (&ld);
+
+  /* querying insul is equivalent to querying first descendant */
+  first_owner = p4est_comm_find_owner (p4est, qtree, insul, rank);
+  /* querying last descendant */
+  p4est_quadrant_last_descendant (insul, &ld, P4EST_QMAXLEVEL);
+  last_owner = p4est_comm_find_owner (p4est, qtree, &ld, rank);
+
+  /* send to all processors possibly intersecting insulation */
+  for (owner = first_owner; owner <= last_owner; ++owner) {
+    if (owner == rank && !inter_tree) {
+      /* do not send to self for the same tree */
+      continue;
+    }
+    if (global_first_quadrant[owner] == global_first_quadrant[owner + 1]) {
+      /* do not send to empty processors */
+      continue;
+    }
+    peer = peers + owner;
+    /* avoid duplicates in the send array */
+    found = 0;
+    for (back = 0; back < P4EST_INSUL - 1; ++back) {
+      pos = (int) peer->send_first.elem_count - back - 1;
+      if (pos < 0) {
+        break;
+      }
+      s = (p4est_quadrant_t *) sc_array_index_int (&peer->send_first, pos);
+      if (p4est_quadrant_is_equal (s, q) && s->p.piggy2.which_tree == qtree
+          && s->p.piggy2.from_tree == q->p.piggy2.from_tree
+          && s->pad16 == q->pad16) {
+        found = 1;
+        break;
+      }
+    }
+    if (found) {
+      continue;
+    }
+
+    /* copy quadrant into shipping list */
+    s = p4est_quadrant_array_push (&peer->send_first);
+    *s = *q;
+    s->p.piggy2.which_tree = qtree;     /* piggy back tree id */
+
+    /* update lowest and highest peer */
+    if (owner != rank) {
+      *first_peer = SC_MIN (owner, *first_peer);
+      *last_peer = SC_MAX (owner, *last_peer);
+    }
+  }
+}
+
+static void
+p4est_balance_response (p4est_t * p4est, p4est_balance_peer_t * peer,
+                        p4est_connect_type_t balance, sc_array_t * borders)
+{
+  sc_array_t         *first_seeds = sc_array_new (sizeof (p4est_quadrant_t));
+
+/* compute and uniqify overlap quadrants */
+  p4est_tree_compute_overlap (p4est, &peer->recv_first,
+                              &peer->send_second, balance, borders,
+                              first_seeds);
+  /* replace peer->recv_first with first_seeds */
+  p4est_tree_uniqify_overlap (&peer->send_second);
+  p4est_tree_uniqify_overlap (first_seeds);
+  /* replace peer->recv_first with first_seeds */
+  sc_array_resize (&peer->recv_first, first_seeds->elem_count);
+  memcpy (peer->recv_first.array, first_seeds->array,
+          first_seeds->elem_size * first_seeds->elem_count);
+  sc_array_destroy (first_seeds);
+
+  if (p4est->inspect) {
+    p4est->inspect->balance_comm_sent += peer->send_second.elem_count;
+    if (peer->send_second.elem_count) {
+      p4est->inspect->balance_comm_nzpeers++;
+    }
+  }
+}
+
+void
+p4est_balance (p4est_t * p4est, p4est_connect_type_t btype,
+               p4est_init_t init_fn)
+{
+  p4est_balance_ext (p4est, btype, init_fn, NULL);
+}
+
+void
+p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype,
+                   p4est_init_t init_fn, p4est_replace_t replace_fn)
+{
+  const int           rank = p4est->mpirank;
+  const int           num_procs = p4est->mpisize;
+  int                 j, k, l, m, which;
+  int                 face;
+  int                 first_peer, last_peer;
+  int                 quad_contact[P4EST_FACES];
+  int                 any_face, tree_contact[P4EST_FACES];
+  int                 tree_fully_owned, full_tree[2];
+  int8_t             *tree_flags;
+  size_t              zz, treecount, ctree;
+  size_t              localcount;
+  size_t              qcount, qbytes;
+  size_t              all_incount, all_outcount;
+  p4est_qcoord_t      qh;
+  const p4est_qcoord_t rh = P4EST_ROOT_LEN;
+  p4est_topidx_t      qtree, nt;
+  p4est_topidx_t      first_tree, last_tree;
+  p4est_locidx_t      skipped;
+  p4est_balance_peer_t *peers, *peer;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t    mylow, nextlow;
+  p4est_quadrant_t    tosend, insulq, tempq;
+  p4est_quadrant_t   *q, *s;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  sc_array_t         *qarray, *tquadrants;
+  sc_array_t         *borders;
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              data_pool_size;
+#endif
+  int                 ftransform[P4EST_FTRANSFORM];
+  int                 face_axis[3];     /* 3 not P4EST_DIM */
+  int                 contact_face_only, contact_edge_only;
+#ifdef P4_TO_P8
+  int                 edge;
+  size_t              etree;
+  p8est_edge_info_t   ei;
+  p8est_edge_transform_t *et;
+  sc_array_t         *eta;
+#endif
+  int                 corner;
+  p4est_corner_info_t ci;
+  p4est_corner_transform_t *ct;
+  sc_array_t         *cta;
+#ifdef P4EST_ENABLE_MPI
+#ifdef P4EST_ENABLE_DEBUG
+  unsigned            checksum;
+  sc_array_t          checkarray;
+  p4est_gloidx_t      ltotal[2], gtotal[2];
+#endif /* P4EST_ENABLE_DEBUG */
+  int                 i;
+  int                 mpiret, rcount;
+  int                 first_bound;
+  int                 request_first_count, request_second_count, outcount;
+  int                 request_send_count, total_send_count, total_recv_count;
+  int                 nwin, maxpeers, maxwin, twomaxwin;
+  int                 send_zero[2], send_load[2];
+  int                 recv_zero[2], recv_load[2];
+  int                 my_ranges[2 * p4est_num_ranges];
+  int                *wait_indices;
+  int                *procs, *all_ranges;
+  int                *receiver_ranks, *sender_ranks;
+  int                 num_receivers, num_senders;
+  int                *receiver_ranks_ranges, *sender_ranks_ranges;
+  int                 num_receivers_ranges, num_senders_ranges;
+  int                *receiver_ranks_notify, *sender_ranks_notify;
+  int                 num_receivers_notify, num_senders_notify;
+  int                 is_ranges_primary, is_balance_verify;
+  int                 is_ranges_active, is_notify_active;
+  int                 max_ranges;
+  MPI_Request        *requests_first, *requests_second;
+  MPI_Request        *send_requests_first_count, *send_requests_first_load;
+  MPI_Request        *send_requests_second_count, *send_requests_second_load;
+  MPI_Status         *recv_statuses, *jstatus;
+#endif /* P4EST_ENABLE_MPI */
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING
+                            "_balance %s with %lld total quadrants\n",
+                            p4est_connect_type_string (btype),
+                            (long long) p4est->global_num_quadrants);
+  p4est_log_indent_push ();
+  P4EST_ASSERT (p4est_is_valid (p4est));
+#ifndef P4_TO_P8
+  P4EST_ASSERT (btype == P4EST_CONNECT_FACE || btype == P4EST_CONNECT_CORNER);
+#else
+  P4EST_ASSERT (btype == P8EST_CONNECT_FACE || btype == P8EST_CONNECT_EDGE ||
+                btype == P8EST_CONNECT_CORNER);
+#endif
+
+#ifdef P4EST_ENABLE_DEBUG
+  data_pool_size = 0;
+  if (p4est->user_data_pool != NULL) {
+    data_pool_size = p4est->user_data_pool->elem_count;
+  }
+#endif
+
+  P4EST_QUADRANT_INIT (&mylow);
+  P4EST_QUADRANT_INIT (&nextlow);
+  P4EST_QUADRANT_INIT (&tosend);
+  P4EST_QUADRANT_INIT (&insulq);
+  P4EST_QUADRANT_INIT (&tempq);
+
+  /* tree status flags (max 8 per tree) */
+  tree_flags = P4EST_ALLOC (int8_t, conn->num_trees);
+  for (nt = 0; nt < conn->num_trees; ++nt) {
+    tree_flags[nt] = 0x00;
+  }
+
+  localcount = (size_t) (p4est->last_local_tree + 1 -
+                         p4est->first_local_tree);
+  borders = sc_array_new_size (sizeof (sc_array_t), localcount);
+  for (zz = 0; zz < localcount; zz++) {
+    qarray = (sc_array_t *) sc_array_index (borders, zz);
+    sc_array_init (qarray, sizeof (p4est_quadrant_t));
+  }
+
+#ifdef P4EST_ENABLE_MPI
+  requests_first = P4EST_ALLOC (MPI_Request, 6 * num_procs);
+  requests_second = requests_first + 1 * num_procs;
+  send_requests_first_count = requests_first + 2 * num_procs;
+  send_requests_first_load = requests_first + 3 * num_procs;
+  send_requests_second_count = requests_first + 4 * num_procs;
+  send_requests_second_load = requests_first + 5 * num_procs;
+  recv_statuses = P4EST_ALLOC (MPI_Status, num_procs);
+  for (j = 0; j < num_procs; ++j) {
+    requests_first[j] = requests_second[j] = MPI_REQUEST_NULL;
+    send_requests_first_count[j] = MPI_REQUEST_NULL;
+    send_requests_first_load[j] = MPI_REQUEST_NULL;
+    send_requests_second_count[j] = MPI_REQUEST_NULL;
+    send_requests_second_load[j] = MPI_REQUEST_NULL;
+  }
+  wait_indices = P4EST_ALLOC (int, num_procs);
+#ifdef P4EST_ENABLE_DEBUG
+  sc_array_init (&checkarray, 4);
+#endif /* P4EST_ENABLE_DEBUG */
+#endif /* P4EST_ENABLE_MPI */
+
+  /* allocate per peer storage and initialize requests */
+  peers = P4EST_ALLOC (p4est_balance_peer_t, num_procs);
+  for (j = 0; j < num_procs; ++j) {
+    peer = peers + j;
+    sc_array_init (&peer->send_first, sizeof (p4est_quadrant_t));
+    sc_array_init (&peer->send_second, sizeof (p4est_quadrant_t));
+    sc_array_init (&peer->recv_first, sizeof (p4est_quadrant_t));
+    sc_array_init (&peer->recv_second, sizeof (p4est_quadrant_t));
+    peer->send_first_count = peer->send_second_count = 0;
+    peer->recv_first_count = peer->recv_second_count = 0;
+    peer->have_first_count = peer->have_first_load = 0;
+    peer->have_second_count = peer->have_second_load = 0;
+  }
+#ifdef P4_TO_P8
+  eta = &ei.edge_transforms;
+  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+#endif
+  cta = &ci.corner_transforms;
+  sc_array_init (cta, sizeof (p4est_corner_transform_t));
+
+  /* compute first quadrant on finest level */
+  mylow.x = p4est->global_first_position[rank].x;
+  mylow.y = p4est->global_first_position[rank].y;
+#ifdef P4_TO_P8
+  mylow.z = p4est->global_first_position[rank].z;
+#endif
+  mylow.level = P4EST_QMAXLEVEL;
+
+  /* and the first finest quadrant of the next processor */
+  nextlow.x = p4est->global_first_position[rank + 1].x;
+  nextlow.y = p4est->global_first_position[rank + 1].y;
+#ifdef P4_TO_P8
+  nextlow.z = p4est->global_first_position[rank + 1].z;
+#endif
+  nextlow.level = P4EST_QMAXLEVEL;
+
+  /* start balance_A timing */
+  if (p4est->inspect != NULL) {
+    p4est->inspect->balance_A = -sc_MPI_Wtime ();
+    p4est->inspect->balance_A_count_in = 0;
+    p4est->inspect->balance_A_count_out = 0;
+    p4est->inspect->use_B = 0;
+  }
+
+  /* loop over all local trees to assemble first send list */
+  first_tree = p4est->first_local_tree;
+  last_tree = p4est->last_local_tree;
+  first_peer = num_procs;
+  last_peer = -1;
+  all_incount = 0;
+  skipped = 0;
+  for (nt = first_tree; nt <= last_tree; ++nt) {
+    p4est_comm_tree_info (p4est, nt, full_tree, tree_contact, NULL, NULL);
+    tree_fully_owned = full_tree[0] && full_tree[1];
+    any_face = 0;
+    for (face = 0; face < P4EST_FACES; ++face) {
+      any_face = any_face || tree_contact[face];
+    }
+    if (any_face) {
+      tree_flags[nt] |= any_face_flag;
+    }
+    tree = p4est_tree_array_index (p4est->trees, nt);
+    tquadrants = &tree->quadrants;
+    all_incount += tquadrants->elem_count;
+
+    /* initial log message for this tree */
+    P4EST_VERBOSEF ("Into balance tree %lld with %llu\n", (long long) nt,
+                    (unsigned long long) tquadrants->elem_count);
+
+    /* local balance first pass */
+    p4est_balance_subtree_ext (p4est, btype, nt, init_fn, replace_fn);
+    treecount = tquadrants->elem_count;
+    P4EST_VERBOSEF ("Balance tree %lld A %llu\n",
+                    (long long) nt, (unsigned long long) treecount);
+
+    /* check if this tree is not shared with other processors */
+    if (tree_fully_owned) {
+      /* all quadrants in this tree are owned by me */
+      tree_flags[nt] |= fully_owned_flag;
+      if (!any_face) {
+        /* this tree is isolated, no balance between trees */
+        continue;
+      }
+    }
+
+    if (borders != NULL) {
+      qarray = (sc_array_t *) sc_array_index (borders,
+                                              (size_t) (nt - first_tree));
+    }
+    else {
+      qarray = NULL;
+    }
+
+    /* identify boundary quadrants and prepare them to be sent */
+    for (zz = 0; zz < treecount; ++zz) {
+      /* this quadrant may be on the boundary with a range of processors */
+      q = p4est_quadrant_array_index (tquadrants, zz);
+      qh = P4EST_QUADRANT_LEN (q->level);
+      if (p4est_comm_neighborhood_owned (p4est, nt,
+                                         full_tree, tree_contact, q)) {
+        /* this quadrant's 3x3 neighborhood is onwed by this processor */
+        ++skipped;
+        continue;
+      }
+
+      if (qarray != NULL) {
+        s = (p4est_quadrant_t *) sc_array_push (qarray);
+        *s = *q;
+      }
+
+#ifdef P4_TO_P8
+      for (m = 0; m < 3; ++m) {
+#if 0
+      }
+#endif
+#else
+      m = 0;
+#endif
+      for (k = 0; k < 3; ++k) {
+        for (l = 0; l < 3; ++l) {
+          which = m * 9 + k * 3 + l;    /* 2D: 0..8, 3D: 0..26 */
+          /* exclude myself from the queries */
+          if (which == P4EST_INSUL / 2) {
+            continue;
+          }
+          /* may modify insulq below, never modify q itself! */
+          insulq = *q;
+          insulq.x += (l - 1) * qh;
+          insulq.y += (k - 1) * qh;
+#ifdef P4_TO_P8
+          insulq.z += (m - 1) * qh;
+#endif
+          /* check boundary status of insulation quadrant */
+          quad_contact[0] = (insulq.x < 0);
+          quad_contact[1] = (insulq.x >= rh);
+          face_axis[0] = quad_contact[0] || quad_contact[1];
+          quad_contact[2] = (insulq.y < 0);
+          quad_contact[3] = (insulq.y >= rh);
+          face_axis[1] = quad_contact[2] || quad_contact[3];
+#ifndef P4_TO_P8
+          face_axis[2] = 0;
+#else
+          quad_contact[4] = (insulq.z < 0);
+          quad_contact[5] = (insulq.z >= rh);
+          face_axis[2] = quad_contact[4] || quad_contact[5];
+          edge = -1;
+#endif
+          contact_edge_only = contact_face_only = 0;
+          face = -1;
+          if (face_axis[0] || face_axis[1] || face_axis[2]) {
+            /* this quadrant is relevant for inter-tree balancing */
+            if (!face_axis[1] && !face_axis[2]) {
+              contact_face_only = 1;
+              face = 0 + quad_contact[1];
+            }
+            else if (!face_axis[0] && !face_axis[2]) {
+              contact_face_only = 1;
+              face = 2 + quad_contact[3];
+            }
+#ifdef P4_TO_P8
+            else if (!face_axis[0] && !face_axis[1]) {
+              contact_face_only = 1;
+              face = 4 + quad_contact[5];
+            }
+            else if (!face_axis[0]) {
+              contact_edge_only = 1;
+              edge = 0 + 2 * quad_contact[5] + quad_contact[3];
+            }
+            else if (!face_axis[1]) {
+              contact_edge_only = 1;
+              edge = 4 + 2 * quad_contact[5] + quad_contact[1];
+            }
+            else if (!face_axis[2]) {
+              contact_edge_only = 1;
+              edge = 8 + 2 * quad_contact[3] + quad_contact[1];
+            }
+#endif
+            if (contact_face_only) {
+              /* square contact across a face */
+              P4EST_ASSERT (!contact_edge_only);
+              P4EST_ASSERT (face >= 0 && face < P4EST_FACES);
+              P4EST_ASSERT (quad_contact[face]);
+              qtree = p4est_find_face_transform (conn, nt, face, ftransform);
+              if (qtree >= 0) {
+                P4EST_ASSERT (tree_contact[face]);
+                p4est_quadrant_transform_face (q, &tosend, ftransform);
+                tosend.p.piggy2.from_tree = nt;
+                tosend.pad16 = face;
+                p4est_quadrant_transform_face (&insulq, &tempq, ftransform);
+                p4est_balance_schedule (p4est, peers, qtree, 1,
+                                        &tosend, &tempq,
+                                        &first_peer, &last_peer);
+              }
+              else {
+                /* goes across a face with no neighbor */
+                P4EST_ASSERT (!tree_contact[face]);
+              }
+            }
+#ifdef P4_TO_P8
+            else if (contact_edge_only) {
+              /* this quadrant crosses an edge */
+              P4EST_ASSERT (!contact_face_only);
+              P4EST_ASSERT (edge >= 0 && edge < P8EST_EDGES);
+              p8est_find_edge_transform (conn, nt, edge, &ei);
+              for (etree = 0; etree < eta->elem_count; ++etree) {
+                et = p8est_edge_array_index (eta, etree);
+                p8est_quadrant_transform_edge (q, &tosend, &ei, et, 0);
+                tosend.p.piggy2.from_tree = nt;
+                tosend.pad16 = edge;
+                p8est_quadrant_transform_edge (&insulq, &tempq, &ei, et, 1);
+                p4est_balance_schedule (p4est, peers, et->ntree, 1,
+                                        &tosend, &tempq,
+                                        &first_peer, &last_peer);
+              }
+            }
+#endif
+            else {
+              /* this quadrant crosses a corner */
+              P4EST_ASSERT (face_axis[0] && face_axis[1]);
+              corner = quad_contact[1] + 2 * quad_contact[3];
+#ifdef P4_TO_P8
+              P4EST_ASSERT (face_axis[2]);
+              corner += 4 * quad_contact[5];
+#endif
+              P4EST_ASSERT (p4est_quadrant_touches_corner (q, corner, 1));
+              P4EST_ASSERT (p4est_quadrant_touches_corner
+                            (&insulq, corner, 0));
+              p4est_find_corner_transform (conn, nt, corner, &ci);
+              for (ctree = 0; ctree < cta->elem_count; ++ctree) {
+                ct = p4est_corner_array_index (cta, ctree);
+                tosend = *q;
+                p4est_quadrant_transform_corner (&tosend, (int) ct->ncorner,
+                                                 0);
+                tosend.p.piggy2.from_tree = nt;
+                tosend.pad16 = corner;
+                tempq = insulq;
+                p4est_quadrant_transform_corner (&tempq, (int) ct->ncorner,
+                                                 1);
+                p4est_balance_schedule (p4est, peers, ct->ntree, 1,
+                                        &tosend, &tempq, &first_peer,
+                                        &last_peer);
+              }
+            }
+          }
+          else {
+            /* no inter-tree contact */
+            tosend = *q;
+            tosend.p.piggy2.from_tree = nt;
+            tosend.pad16 = -1;
+            p4est_balance_schedule (p4est, peers, nt, 0,
+                                    &tosend, &insulq, &first_peer,
+                                    &last_peer);
+          }
+        }
+      }
+#ifdef P4_TO_P8
+#if 0
+      {
+#endif
+      }
+#endif
+    }
+    tquadrants = NULL;          /* safeguard */
+  }
+
+  /* end balance_A, start balance_comm */
+#ifdef P4EST_ENABLE_MPI
+  is_ranges_primary = 0;
+  is_ranges_active = 0;
+  is_notify_active = 1;
+  is_balance_verify = 0;
+#endif
+  if (p4est->inspect != NULL) {
+    p4est->inspect->balance_A += sc_MPI_Wtime ();
+    p4est->inspect->balance_comm = -sc_MPI_Wtime ();
+    p4est->inspect->balance_comm_sent = 0;
+    p4est->inspect->balance_comm_nzpeers = 0;
+    for (k = 0; k < 2; ++k) {
+      p4est->inspect->balance_zero_sends[k] = 0;
+      p4est->inspect->balance_zero_receives[k] = 0;
+    }
+    p4est->inspect->balance_ranges = 0.;
+    p4est->inspect->balance_notify = 0.;
+    p4est->inspect->balance_notify_allgather = 0.;
+#ifdef P4EST_ENABLE_MPI
+    is_ranges_primary = p4est->inspect->use_balance_ranges;
+    is_ranges_active = is_ranges_primary;
+    is_notify_active = !is_ranges_primary;
+    if (p4est->inspect->use_balance_ranges_notify) {
+      is_ranges_active = is_notify_active = 1;
+    }
+    is_balance_verify = p4est->inspect->use_balance_verify;
+#endif
+  }
+
+#ifdef P4EST_ENABLE_MPI
+  /* encode and distribute the asymmetric communication pattern */
+  procs = NULL;
+  receiver_ranks = sender_ranks = NULL;
+  num_receivers = num_senders = 0;
+  receiver_ranks_ranges = sender_ranks_ranges = NULL;
+  num_receivers_ranges = num_senders_ranges = 0;
+  receiver_ranks_notify = sender_ranks_notify = NULL;
+  num_receivers_notify = num_senders_notify = 0;
+
+  /* determine asymmetric communication pattern by sc_ranges function */
+  if (is_ranges_active) {
+    procs = P4EST_ALLOC (int, num_procs);
+    receiver_ranks_ranges = P4EST_ALLOC (int, num_procs);
+    sender_ranks_ranges = P4EST_ALLOC (int, num_procs);
+
+    for (j = 0; j < num_procs; ++j) {
+      procs[j] = (int) peers[j].send_first.elem_count;
+    }
+    maxpeers = first_peer;
+    maxwin = last_peer;
+    max_ranges = p4est_num_ranges;
+    if (p4est->inspect != NULL) {
+      if (p4est->inspect->balance_max_ranges > 0 &&
+          p4est->inspect->balance_max_ranges < p4est_num_ranges) {
+        max_ranges = p4est->inspect->balance_max_ranges;
+      }
+      p4est->inspect->balance_ranges = -MPI_Wtime ();
+    }
+    nwin = sc_ranges_adaptive (p4est_package_id,
+                               p4est->mpicomm, procs, &maxpeers, &maxwin,
+                               max_ranges, my_ranges, &all_ranges);
+    twomaxwin = 2 * maxwin;
+    if (p4est->inspect != NULL) {
+      p4est->inspect->balance_ranges += MPI_Wtime ();
+    }
+    sc_ranges_decode (num_procs, rank, maxwin, all_ranges,
+                      &num_receivers_ranges, receiver_ranks_ranges,
+                      &num_senders_ranges, sender_ranks_ranges);
+    if (is_balance_verify) {
+      /* verification written after using sc_ranges_decode */
+      k = 0;
+      for (j = 0; j < num_procs; ++j) {
+        if (j == rank) {
+          continue;
+        }
+        if (procs[j] > 0) {
+          P4EST_ASSERT (k < num_receivers_ranges &&
+                        receiver_ranks_ranges[k] == j);
+          ++k;
+        }
+        else {
+          if (k < num_receivers_ranges && receiver_ranks_ranges[k] == j) {
+            ++k;
+          }
+        }
+      }
+      P4EST_ASSERT (k == num_receivers_ranges);
+
+      /* original verification loop modified and partially redundant */
+      k = 0;
+      for (j = first_peer; j <= last_peer; ++j) {
+        if (j == rank) {
+          P4EST_ASSERT (k == num_receivers_ranges ||
+                        receiver_ranks_ranges[k] != j);
+          continue;
+        }
+        peer = peers + j;
+        qcount = peer->send_first.elem_count;
+
+        for (i = 0; i < nwin - 1; ++i) {
+          if (j > my_ranges[2 * i + 1] && j < my_ranges[2 * (i + 1)]) {
+            break;
+          }
+        }
+        if (i < nwin - 1) {
+          P4EST_ASSERT (qcount == 0);
+          P4EST_ASSERT (k == num_receivers_ranges ||
+                        receiver_ranks_ranges[k] != j);
+          continue;
+        }
+        P4EST_ASSERT (k < num_receivers_ranges &&
+                      receiver_ranks_ranges[k] == j);
+        ++k;
+      }
+      P4EST_ASSERT (k == num_receivers_ranges);
+
+      /* original verification loop of who is sending to me */
+      k = 0;
+      for (j = 0; j < num_procs; ++j) {
+        if (j == rank) {
+          P4EST_ASSERT (k == num_senders_ranges ||
+                        sender_ranks_ranges[k] != j);
+          continue;
+        }
+        for (i = 0; i < maxwin; ++i) {
+          first_bound = all_ranges[twomaxwin * j + 2 * i];
+          if (first_bound == -1 || first_bound > rank) {
+            P4EST_ASSERT (k == num_senders_ranges ||
+                          sender_ranks_ranges[k] != j);
+            break;
+          }
+          if (rank <= all_ranges[twomaxwin * j + 2 * i + 1]) {
+            /* processor j is sending to me */
+            P4EST_ASSERT (k < num_senders_ranges &&
+                          sender_ranks_ranges[k] == j);
+            ++k;
+            break;
+          }
+        }
+      }
+      P4EST_ASSERT (k == num_senders_ranges);
+    }
+#ifdef P4EST_ENABLE_DEBUG
+    P4EST_GLOBAL_STATISTICSF ("Max peers %d ranges %d/%d\n",
+                              maxpeers, maxwin, max_ranges);
+    sc_ranges_statistics (p4est_package_id, SC_LP_STATISTICS,
+                          p4est->mpicomm, num_procs, procs,
+                          rank, max_ranges, my_ranges);
+#endif
+    SC_FREE (all_ranges);
+    P4EST_FREE (procs);
+    P4EST_VERBOSEF ("Peer ranges %d/%d/%d first %d last %d\n",
+                    nwin, maxwin, max_ranges, first_peer, last_peer);
+  }
+
+  /* determine asymmetric communication pattern by sc_notify function */
+  if (is_notify_active) {
+    receiver_ranks_notify = P4EST_ALLOC (int, num_procs);
+    sender_ranks_notify = P4EST_ALLOC (int, num_procs);
+    num_receivers_notify = num_senders_notify = 0;
+
+    for (j = 0; j < num_procs; ++j) {
+      if (j != rank && peers[j].send_first.elem_count > 0) {
+        receiver_ranks_notify[num_receivers_notify++] = j;
+      }
+    }
+    if (p4est->inspect != NULL) {
+      p4est->inspect->balance_notify = -MPI_Wtime ();
+    }
+    mpiret = sc_notify (receiver_ranks_notify, num_receivers_notify,
+                        sender_ranks_notify, &num_senders_notify,
+                        p4est->mpicomm);
+    SC_CHECK_MPI (mpiret);
+    if (p4est->inspect != NULL) {
+      p4est->inspect->balance_notify += MPI_Wtime ();
+    }
+
+    /* double-check sc_notify results by sc_notify_allgather */
+    if (is_balance_verify) {
+      int                *sender_ranks2, num_senders2;
+
+      sender_ranks2 = P4EST_ALLOC (int, num_procs);
+      if (p4est->inspect != NULL) {
+        p4est->inspect->balance_notify_allgather = -MPI_Wtime ();
+      }
+      mpiret = sc_notify_allgather (receiver_ranks_notify,
+                                    num_receivers_notify,
+                                    sender_ranks2, &num_senders2,
+                                    p4est->mpicomm);
+      SC_CHECK_MPI (mpiret);
+      if (p4est->inspect != NULL) {
+        p4est->inspect->balance_notify_allgather += MPI_Wtime ();
+      }
+
+      /* run verification against sc_notify_allgather */
+      SC_CHECK_ABORT (num_senders2 == num_senders_notify,
+                      "Failed notify_allgather sender count");
+      for (j = 0; j < num_senders_notify; ++j) {
+        SC_CHECK_ABORT (sender_ranks2[j] == sender_ranks_notify[j],
+                        "Failed notify_allgather sender rank");
+      }
+      P4EST_FREE (sender_ranks2);
+    }
+  }
+
+  /* verify sc_ranges and sc_notify against each other */
+  if (is_ranges_active && is_notify_active && is_balance_verify) {
+    int                 found_in_ranges, found_in_notify;
+
+    /* verify receiver side */
+    P4EST_ASSERT (num_receivers_notify <= num_receivers_ranges);
+    k = l = 0;
+    for (j = 0; j < num_procs; ++j) {
+      found_in_ranges = found_in_notify = 0;
+      if (k < num_receivers_ranges && receiver_ranks_ranges[k] == j) {
+        P4EST_ASSERT (j != rank);
+        found_in_ranges = 1;
+        ++k;
+      }
+      if (l < num_receivers_notify && receiver_ranks_notify[l] == j) {
+        P4EST_ASSERT (j != rank && found_in_ranges);
+        found_in_notify = 1;
+        ++l;
+      }
+      if (j != rank && peers[j].send_first.elem_count > 0) {
+        P4EST_ASSERT (found_in_ranges && found_in_notify);
+      }
+      if (peers[j].send_first.elem_count == 0) {
+        P4EST_ASSERT (!found_in_notify);
+      }
+    }
+    P4EST_ASSERT (k == num_receivers_ranges);
+    P4EST_ASSERT (l == num_receivers_notify);
+
+    /* verify sender side */
+    P4EST_ASSERT (num_senders_notify <= num_senders_ranges);
+    k = l = 0;
+    for (j = 0; j < num_procs; ++j) {
+      found_in_ranges = found_in_notify = 0;
+      if (k < num_senders_ranges && sender_ranks_ranges[k] == j) {
+        P4EST_ASSERT (j != rank);
+        found_in_ranges = 1;
+        ++k;
+      }
+      if (l < num_senders_notify && sender_ranks_notify[l] == j) {
+        P4EST_ASSERT (j != rank && found_in_ranges);
+        found_in_notify = 1;    /* kept for symmetry */
+        ++l;
+      }
+    }
+    P4EST_ASSERT (k == num_senders_ranges);
+    P4EST_ASSERT (l == num_senders_notify);
+  }
+
+  /*
+   * loop over all peers and send first round of quadrants
+   * for intra-tree balancing, each load is contained in one tree
+   */
+  total_send_count = total_recv_count = 0;
+  request_first_count = request_second_count = request_send_count = 0;
+  send_zero[0] = send_load[0] = recv_zero[0] = recv_load[0] = 0;
+  send_zero[1] = send_load[1] = recv_zero[1] = recv_load[1] = 0;
+  if (is_ranges_primary) {
+    P4EST_ASSERT (is_ranges_active);
+    receiver_ranks = receiver_ranks_ranges;
+    sender_ranks = sender_ranks_ranges;
+    num_receivers = num_receivers_ranges;
+    num_senders = num_senders_ranges;
+  }
+  else {
+    P4EST_ASSERT (is_notify_active);
+    receiver_ranks = receiver_ranks_notify;
+    sender_ranks = sender_ranks_notify;
+    num_receivers = num_receivers_notify;
+    num_senders = num_senders_notify;
+  }
+  P4EST_ASSERT (receiver_ranks != NULL && sender_ranks != NULL);
+  num_receivers_ranges = num_senders_ranges = 0;
+  num_receivers_notify = num_senders_notify = 0;
+
+  /* Use receiver_ranks array to send to them */
+  for (k = 0; k < num_receivers; ++k) {
+    j = receiver_ranks[k];
+    P4EST_ASSERT (j >= first_peer && j <= last_peer && j != rank);
+    peer = peers + j;
+    qcount = peer->send_first.elem_count;
+
+    /* first send number of quadrants to be expected */
+    if (qcount > 0) {
+      P4EST_LDEBUGF ("Balance A send %llu quadrants to %d\n",
+                     (unsigned long long) qcount, j);
+      ++send_load[0];
+    }
+    else {
+      P4EST_ASSERT (is_ranges_primary);
+      ++send_zero[0];
+    }
+    peer->send_first_count = (int) qcount;
+    mpiret = MPI_Isend (&peer->send_first_count, 1, MPI_INT,
+                        j, P4EST_COMM_BALANCE_FIRST_COUNT,
+                        p4est->mpicomm, &send_requests_first_count[j]);
+    SC_CHECK_MPI (mpiret);
+    ++request_send_count;
+
+    /* sort and send the actual quadrants and post receive for reply */
+    if (qcount > 0) {
+      sc_array_sort (&peer->send_first, p4est_quadrant_compare_piggy);
+
+#ifdef P4EST_ENABLE_DEBUG
+      checksum = p4est_quadrant_checksum (&peer->send_first, &checkarray, 0);
+      P4EST_LDEBUGF ("Balance A send checksum 0x%08x to %d\n", checksum, j);
+#endif /* P4EST_ENABLE_DEBUG */
+
+      total_send_count += qcount;
+      qbytes = qcount * sizeof (p4est_quadrant_t);
+      mpiret = MPI_Isend (peer->send_first.array, (int) qbytes, MPI_BYTE,
+                          j, P4EST_COMM_BALANCE_FIRST_LOAD,
+                          p4est->mpicomm, &send_requests_first_load[j]);
+      SC_CHECK_MPI (mpiret);
+      ++request_send_count;
+      mpiret = MPI_Irecv (&peer->recv_second_count, 1, MPI_INT,
+                          j, P4EST_COMM_BALANCE_SECOND_COUNT,
+                          p4est->mpicomm, &requests_second[j]);
+      SC_CHECK_MPI (mpiret);
+      ++request_second_count;
+    }
+  }
+  peer = NULL;
+  P4EST_FREE (receiver_ranks_ranges);
+  P4EST_FREE (receiver_ranks_notify);
+  receiver_ranks = receiver_ranks_ranges = receiver_ranks_notify = NULL;
+
+  /* find out who is sending to me and receive quadrant counts */
+  for (k = 0; k < num_senders; ++k) {
+    j = sender_ranks[k];
+    ++request_first_count;
+    mpiret = MPI_Irecv (&peers[j].recv_first_count, 1, MPI_INT,
+                        j, P4EST_COMM_BALANCE_FIRST_COUNT,
+                        p4est->mpicomm, &requests_first[j]);
+    SC_CHECK_MPI (mpiret);
+  }
+  P4EST_FREE (sender_ranks_ranges);
+  P4EST_FREE (sender_ranks_notify);
+  sender_ranks = sender_ranks_ranges = sender_ranks_notify = NULL;
+
+  /* wait for quadrant counts and post receive and send for quadrants */
+  while (request_first_count > 0) {
+    mpiret = MPI_Waitsome (num_procs, requests_first,
+                           &outcount, wait_indices, recv_statuses);
+    SC_CHECK_MPI (mpiret);
+    P4EST_ASSERT (outcount != MPI_UNDEFINED);
+    P4EST_ASSERT (outcount > 0);
+    for (i = 0; i < outcount; ++i) {
+      /* retrieve sender's rank */
+      j = wait_indices[i];
+      jstatus = &recv_statuses[i];
+      wait_indices[i] = -1;
+      P4EST_ASSERT (j != rank && 0 <= j && j < num_procs);
+      P4EST_ASSERT (requests_first[j] == MPI_REQUEST_NULL);
+      P4EST_ASSERT (jstatus->MPI_SOURCE == j);
+
+      /* check if we are in receiving count or load */
+      peer = peers + j;
+      P4EST_ASSERT (!peer->have_first_load);
+      if (!peer->have_first_count) {
+        /* verify message size */
+        P4EST_ASSERT (jstatus->MPI_TAG == P4EST_COMM_BALANCE_FIRST_COUNT);
+        mpiret = MPI_Get_count (jstatus, MPI_INT, &rcount);
+        SC_CHECK_MPI (mpiret);
+        SC_CHECK_ABORTF (rcount == 1, "Receive count mismatch A %d", rcount);
+
+        /* process the count information received */
+        peer->have_first_count = 1;
+        qcount = (size_t) peer->recv_first_count;
+        if (qcount > 0) {
+          /* received nonzero count, post receive for load */
+          P4EST_LDEBUGF ("Balance A recv %llu quadrants from %d\n",
+                         (unsigned long long) qcount, j);
+          P4EST_ASSERT (peer->recv_first.elem_count == 0);
+          sc_array_resize (&peer->recv_first, qcount);
+          total_recv_count += qcount;
+          qbytes = qcount * sizeof (p4est_quadrant_t);
+          P4EST_ASSERT (requests_first[j] == MPI_REQUEST_NULL);
+          mpiret = MPI_Irecv (peer->recv_first.array, (int) qbytes, MPI_BYTE,
+                              j, P4EST_COMM_BALANCE_FIRST_LOAD,
+                              p4est->mpicomm, &requests_first[j]);
+          SC_CHECK_MPI (mpiret);
+          ++recv_load[0];
+        }
+        else {
+          /* will not receive load, close this request */
+          P4EST_ASSERT (qcount == 0);
+          P4EST_ASSERT (requests_first[j] == MPI_REQUEST_NULL);
+          --request_first_count;
+          ++recv_zero[0];
+        }
+      }
+      else {
+        /* verify received size */
+        P4EST_ASSERT (jstatus->MPI_TAG == P4EST_COMM_BALANCE_FIRST_LOAD);
+        P4EST_ASSERT (peer->recv_first_count > 0);
+        mpiret = MPI_Get_count (jstatus, MPI_BYTE, &rcount);
+        SC_CHECK_MPI (mpiret);
+        SC_CHECK_ABORTF (rcount ==
+                         peer->recv_first_count *
+                         (int) sizeof (p4est_quadrant_t),
+                         "Receive load mismatch A %d %dx%llu", rcount,
+                         peer->recv_first_count,
+                         (unsigned long long) sizeof (p4est_quadrant_t));
+
+        /* received load, close this request */
+        peer->have_first_load = 1;
+        P4EST_ASSERT (requests_first[j] == MPI_REQUEST_NULL);
+        --request_first_count;
+
+#ifdef P4EST_ENABLE_DEBUG
+        checksum =
+          p4est_quadrant_checksum (&peer->recv_first, &checkarray, 0);
+        P4EST_LDEBUGF ("Balance A recv checksum 0x%08x from %d\n", checksum,
+                       j);
+#endif /* P4EST_ENABLE_DEBUG */
+
+        /* process incoming quadrants to interleave with communication */
+        p4est_balance_response (p4est, peer, btype, borders);
+        qcount = peer->send_second.elem_count;
+        if (qcount > 0) {
+          P4EST_LDEBUGF ("Balance B send %llu quadrants to %d\n",
+                         (unsigned long long) qcount, j);
+          ++send_load[1];
+        }
+        else {
+          ++send_zero[1];
+        }
+        peer->send_second_count = (int) qcount;
+        mpiret = MPI_Isend (&peer->send_second_count, 1, MPI_INT,
+                            j, P4EST_COMM_BALANCE_SECOND_COUNT,
+                            p4est->mpicomm, &send_requests_second_count[j]);
+        SC_CHECK_MPI (mpiret);
+        ++request_send_count;
+        if (qcount > 0) {
+
+#ifdef P4EST_ENABLE_DEBUG
+          checksum =
+            p4est_quadrant_checksum (&peer->send_second, &checkarray, 0);
+          P4EST_LDEBUGF ("Balance B send checksum 0x%08x to %d\n", checksum,
+                         j);
+#endif /* P4EST_ENABLE_DEBUG */
+
+          total_send_count += qcount;
+          qbytes = qcount * sizeof (p4est_quadrant_t);
+          mpiret = MPI_Isend (peer->send_second.array, (int) qbytes, MPI_BYTE,
+                              j, P4EST_COMM_BALANCE_SECOND_LOAD,
+                              p4est->mpicomm, &send_requests_second_load[j]);
+          SC_CHECK_MPI (mpiret);
+          ++request_send_count;
+        }
+      }
+    }
+  }
+  for (j = 0; j < num_procs; ++j) {
+    P4EST_ASSERT (requests_first[j] == MPI_REQUEST_NULL);
+  }
+#endif /* P4EST_ENABLE_MPI */
+
+  /* simulate send and receive with myself across tree boundaries */
+  peer = peers + rank;
+  sc_array_sort (&peer->send_first, p4est_quadrant_compare_piggy);
+  qcount = peer->send_first.elem_count;
+  peer->recv_first_count = peer->send_first_count = (int) qcount;
+  qbytes = qcount * sizeof (p4est_quadrant_t);
+  qarray = &peer->recv_first;
+  sc_array_resize (qarray, qcount);
+  memcpy (qarray->array, peer->send_first.array, qbytes);
+  p4est_balance_response (p4est, peer, btype, borders);
+  qcount = peer->send_second.elem_count;
+  peer->recv_second_count = peer->send_second_count = (int) qcount;
+  qbytes = qcount * sizeof (p4est_quadrant_t);
+  qarray = &peer->recv_second;
+  sc_array_resize (qarray, qcount);
+  memcpy (qarray->array, peer->send_second.array, qbytes);
+
+#ifdef P4EST_ENABLE_MPI
+  /* receive second round appending to the same receive buffer */
+  while (request_second_count > 0) {
+    mpiret = MPI_Waitsome (num_procs, requests_second,
+                           &outcount, wait_indices, recv_statuses);
+    SC_CHECK_MPI (mpiret);
+    P4EST_ASSERT (outcount != MPI_UNDEFINED);
+    P4EST_ASSERT (outcount > 0);
+    for (i = 0; i < outcount; ++i) {
+      /* retrieve sender's rank */
+      j = wait_indices[i];
+      jstatus = &recv_statuses[i];
+      wait_indices[i] = -1;
+      P4EST_ASSERT (j != rank && 0 <= j && j < num_procs);
+      P4EST_ASSERT (requests_second[j] == MPI_REQUEST_NULL);
+      P4EST_ASSERT (jstatus->MPI_SOURCE == j);
+
+      /* check if we are in receiving count or load */
+      peer = peers + j;
+      P4EST_ASSERT (!peer->have_second_load);
+      if (!peer->have_second_count) {
+        /* verify message size */
+        P4EST_ASSERT (jstatus->MPI_TAG == P4EST_COMM_BALANCE_SECOND_COUNT);
+        mpiret = MPI_Get_count (jstatus, MPI_INT, &rcount);
+        SC_CHECK_MPI (mpiret);
+        SC_CHECK_ABORTF (rcount == 1, "Receive count mismatch B %d", rcount);
+
+        /* process the count information received */
+        peer->have_second_count = 1;
+        qcount = (size_t) peer->recv_second_count;
+        if (qcount > 0) {
+          /* received nonzero count, post receive for load */
+          P4EST_LDEBUGF ("Balance B recv %llu quadrants from %d\n",
+                         (unsigned long long) qcount, j);
+          P4EST_ASSERT (peer->recv_second.elem_count == 0);
+          sc_array_resize (&peer->recv_second, qcount);
+          total_recv_count += qcount;
+          qbytes = qcount * sizeof (p4est_quadrant_t);
+          P4EST_ASSERT (requests_second[j] == MPI_REQUEST_NULL);
+          mpiret = MPI_Irecv (peer->recv_second.array, (int) qbytes,
+                              MPI_BYTE, j, P4EST_COMM_BALANCE_SECOND_LOAD,
+                              p4est->mpicomm, &requests_second[j]);
+          SC_CHECK_MPI (mpiret);
+          ++recv_load[1];
+        }
+        else {
+          /* will not receive load, close this request */
+          P4EST_ASSERT (qcount == 0);
+          P4EST_ASSERT (requests_second[j] == MPI_REQUEST_NULL);
+          --request_second_count;
+          ++recv_zero[1];
+        }
+      }
+      else {
+        /* verify received size */
+        P4EST_ASSERT (jstatus->MPI_TAG == P4EST_COMM_BALANCE_SECOND_LOAD);
+        P4EST_ASSERT (peer->recv_second_count > 0);
+        mpiret = MPI_Get_count (jstatus, MPI_BYTE, &rcount);
+        SC_CHECK_MPI (mpiret);
+        SC_CHECK_ABORTF (rcount ==
+                         peer->recv_second_count *
+                         (int) sizeof (p4est_quadrant_t),
+                         "Receive load mismatch B %d %dx%llu", rcount,
+                         peer->recv_second_count,
+                         (unsigned long long) sizeof (p4est_quadrant_t));
+
+        /* received load, close this request */
+        peer->have_second_load = 1;
+        P4EST_ASSERT (requests_second[j] == MPI_REQUEST_NULL);
+        --request_second_count;
+
+#ifdef P4EST_ENABLE_DEBUG
+        checksum =
+          p4est_quadrant_checksum (&peer->recv_second, &checkarray, 0);
+        P4EST_LDEBUGF ("Balance B recv checksum 0x%08x from %d\n", checksum,
+                       j);
+#endif /* P4EST_ENABLE_DEBUG */
+      }
+    }
+  }
+  for (j = 0; j < num_procs; ++j) {
+    P4EST_ASSERT (requests_second[j] == MPI_REQUEST_NULL);
+  }
+
+  /* print buffer statistics */
+  P4EST_VERBOSEF ("first send Z %d L %d recv Z %d L %d\n",
+                  send_zero[0], send_load[0], recv_zero[0], recv_load[0]);
+  P4EST_VERBOSEF ("second send Z %d L %d recv Z %d L %d\n",
+                  send_zero[1], send_load[1], recv_zero[1], recv_load[1]);
+  P4EST_VERBOSEF ("total send %d recv %d\n", total_send_count,
+                  total_recv_count);
+  for (j = 0; j < num_procs; ++j) {
+    peer = peers + j;
+    if (peer->send_first.elem_count > 0 || peer->recv_first_count > 0 ||
+        peer->send_second.elem_count > 0 || peer->recv_second_count > 0) {
+      P4EST_VERBOSEF ("peer %d first S %llu R %d second S %llu R %d\n",
+                      j, (unsigned long long) peer->send_first.elem_count,
+                      peer->recv_first_count,
+                      (unsigned long long) peer->send_second.elem_count,
+                      peer->recv_second_count);
+    }
+  }
+#endif /* P4EST_ENABLE_MPI */
+
+  /* end balance_comm, start balance_B */
+  if (p4est->inspect != NULL) {
+    p4est->inspect->balance_comm += sc_MPI_Wtime ();
+    p4est->inspect->balance_B = -sc_MPI_Wtime ();
+    p4est->inspect->balance_B_count_in = 0;
+    p4est->inspect->balance_B_count_out = 0;
+    p4est->inspect->use_B = 1;
+#ifdef P4EST_ENABLE_MPI
+    for (k = 0; k < 2; ++k) {
+      p4est->inspect->balance_zero_sends[k] = send_zero[k];
+      p4est->inspect->balance_zero_receives[k] = recv_zero[k];
+    }
+#endif
+  }
+
+  /* merge received quadrants */
+  for (j = 0; j < num_procs; ++j) {
+    size_t              fcount;
+
+    /* access peer information */
+    peer = peers + j;
+    fcount = peer->recv_first.elem_count;
+    qcount = fcount + peer->recv_second.elem_count;
+    P4EST_ASSERT (peer->send_first_count ==
+                  (int) peer->send_first.elem_count);
+    P4EST_ASSERT (peer->send_second_count ==
+                  (int) peer->send_second.elem_count);
+    P4EST_ASSERT (peer->recv_second_count ==
+                  (int) peer->recv_second.elem_count);
+    if (qcount == 0) {
+      continue;
+    }
+
+    /* merge received quadrants into correct tree */
+    for (zz = 0; zz < qcount; ++zz) {
+      s = zz < fcount ? p4est_quadrant_array_index (&peer->recv_first, zz) :
+        p4est_quadrant_array_index (&peer->recv_second, zz - fcount);
+      P4EST_ASSERT (p4est_quadrant_is_extended (s));
+      qtree = s->p.piggy2.which_tree;
+      if (qtree < first_tree || qtree > last_tree) {
+        /* this is a corner/edge quadrant from the second pass of balance */
+        continue;
+      }
+      if (borders == NULL) {
+        tree = p4est_tree_array_index (p4est->trees, qtree);
+        q = p4est_quadrant_array_push (&tree->quadrants);
+        *q = *s;
+        ++tree->quadrants_per_level[q->level];
+        tree->maxlevel = (int8_t) SC_MAX (tree->maxlevel, q->level);
+        ++p4est->local_num_quadrants;
+        p4est_quadrant_init_data (p4est, qtree, q, init_fn);
+      }
+      else {
+        qarray = (sc_array_t *) sc_array_index (borders,
+                                                (int) (qtree - first_tree));
+        q = p4est_quadrant_array_push (qarray);
+        *q = *s;
+      }
+    }
+  }
+
+  /* rebalance and clamp result back to original tree boundaries */
+  p4est->local_num_quadrants = 0;
+  for (nt = first_tree; nt <= last_tree; ++nt) {
+    /* check if we are the only processor in an isolated tree */
+    tree = p4est_tree_array_index (p4est->trees, nt);
+    tree->quadrants_offset = p4est->local_num_quadrants;
+    tquadrants = &tree->quadrants;
+    treecount = tquadrants->elem_count;
+    if (!(tree_flags[nt] & fully_owned_flag) ||
+        (tree_flags[nt] & any_face_flag)) {
+      /* we have most probably received quadrants, run sort and balance */
+      /* balance the border, add it back into the tree, and linearize */
+      p4est_balance_border (p4est, btype, nt, init_fn, replace_fn, borders);
+      P4EST_VERBOSEF ("Balance tree %lld B %llu to %llu\n",
+                      (long long) nt,
+                      (unsigned long long) treecount,
+                      (unsigned long long) tquadrants->elem_count);
+    }
+    p4est->local_num_quadrants += tquadrants->elem_count;
+    tquadrants = NULL;          /* safeguard */
+  }
+  if (last_tree >= 0) {
+    for (; nt < conn->num_trees; ++nt) {
+      tree = p4est_tree_array_index (p4est->trees, nt);
+      tree->quadrants_offset = p4est->local_num_quadrants;
+    }
+  }
+
+  /* end balance_B */
+  if (p4est->inspect != NULL) {
+    p4est->inspect->balance_B += sc_MPI_Wtime ();
+  }
+
+#ifdef P4EST_ENABLE_MPI
+  /* wait for all send operations */
+  if (request_send_count > 0) {
+    mpiret = MPI_Waitall (4 * num_procs,
+                          send_requests_first_count, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  /* compute global sum of send and receive counts */
+#ifdef P4EST_ENABLE_DEBUG
+  gtotal[0] = gtotal[1] = 0;
+  ltotal[0] = (p4est_gloidx_t) total_send_count;
+  ltotal[1] = (p4est_gloidx_t) total_recv_count;
+  mpiret = MPI_Reduce (ltotal, gtotal, 2, P4EST_MPI_GLOIDX,
+                       MPI_SUM, 0, p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+  P4EST_GLOBAL_STATISTICSF ("Global number of shipped quadrants %lld\n",
+                            (long long) gtotal[0]);
+  P4EST_ASSERT (rank != 0 || gtotal[0] == gtotal[1]);
+#endif /* P4EST_ENABLE_DEBUG */
+#endif /* P4EST_ENABLE_MPI */
+
+  /* loop over all local trees to finalize balance */
+  all_outcount = 0;
+  for (nt = first_tree; nt <= last_tree; ++nt) {
+    tree = p4est_tree_array_index (p4est->trees, nt);
+    all_outcount += tree->quadrants.elem_count;
+
+    /* final log message for this tree */
+    P4EST_VERBOSEF ("Done balance tree %lld now %llu\n", (long long) nt,
+                    (unsigned long long) tree->quadrants.elem_count);
+  }
+
+  /* cleanup temporary storage */
+  P4EST_FREE (tree_flags);
+  for (j = 0; j < num_procs; ++j) {
+    peer = peers + j;
+    sc_array_reset (&peer->send_first);
+    sc_array_reset (&peer->send_second);
+    sc_array_reset (&peer->recv_first);
+    sc_array_reset (&peer->recv_second);
+  }
+  P4EST_FREE (peers);
+
+  if (borders != NULL) {
+    for (zz = 0; zz < localcount; zz++) {
+      qarray = (sc_array_t *) sc_array_index (borders, zz);
+      sc_array_reset (qarray);
+    }
+    sc_array_destroy (borders);
+  }
+
+#ifdef P4_TO_P8
+  sc_array_reset (eta);
+#endif
+  sc_array_reset (cta);
+
+#ifdef P4EST_ENABLE_MPI
+  P4EST_FREE (requests_first);  /* includes allocation for requests_second */
+  P4EST_FREE (recv_statuses);
+  P4EST_FREE (wait_indices);
+#ifdef P4EST_ENABLE_DEBUG
+  sc_array_reset (&checkarray);
+#endif /* P4EST_ENABLE_DEBUG */
+#endif /* P4EST_ENABLE_MPI */
+
+  /* compute global number of quadrants */
+  p4est_comm_count_quadrants (p4est);
+
+  /* some sanity checks */
+  P4EST_ASSERT ((p4est_locidx_t) all_outcount == p4est->local_num_quadrants);
+  P4EST_ASSERT (all_outcount >= all_incount);
+  if (p4est->user_data_pool != NULL) {
+    P4EST_ASSERT (data_pool_size + all_outcount - all_incount ==
+                  p4est->user_data_pool->elem_count);
+  }
+  P4EST_ASSERT (p4est_is_valid (p4est));
+  P4EST_ASSERT (p4est_is_balanced (p4est, btype));
+  P4EST_VERBOSEF ("Balance skipped %lld\n", (long long) skipped);
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done " P4EST_STRING
+                            "_balance with %lld total quadrants\n",
+                            (long long) p4est->global_num_quadrants);
+}
+
+void
+p4est_partition (p4est_t * p4est, int allow_for_coarsening,
+                 p4est_weight_t weight_fn)
+{
+  (void) p4est_partition_ext (p4est, allow_for_coarsening, weight_fn);
+}
+
+p4est_gloidx_t
+p4est_partition_ext (p4est_t * p4est, int partition_for_coarsening,
+                     p4est_weight_t weight_fn)
+{
+  p4est_gloidx_t      global_shipped = 0;
+  const p4est_gloidx_t global_num_quadrants = p4est->global_num_quadrants;
+#ifdef P4EST_ENABLE_MPI
+  int                 mpiret;
+  int                 low_source, high_source;
+  const int           num_procs = p4est->mpisize;
+  const int           rank = p4est->mpirank;
+  const p4est_topidx_t first_tree = p4est->first_local_tree;
+  const p4est_topidx_t last_tree = p4est->last_local_tree;
+  const p4est_locidx_t local_num_quadrants = p4est->local_num_quadrants;
+  int                 i, p;
+  int                 send_lowest, send_highest;
+  int                 num_sends, rcount, base_index;
+  size_t              lz;
+  ssize_t             lowers;
+  p4est_topidx_t      nt;
+  p4est_locidx_t      kl, qlocal;
+  p4est_locidx_t     *num_quadrants_in_proc;
+  p4est_gloidx_t      prev_quadrant, next_quadrant;
+  p4est_gloidx_t      send_index, recv_low, recv_high, qcount;
+  p4est_gloidx_t     *send_array;
+  int64_t             weight, weight_sum;
+  int64_t             cut, my_lowcut, my_highcut;
+  int64_t            *local_weights;    /* cumulative weights by quadrant */
+  int64_t            *global_weight_sums;
+  p4est_quadrant_t   *q;
+  p4est_tree_t       *tree;
+  MPI_Request        *send_requests, recv_requests[2];
+  MPI_Status          recv_statuses[2];
+  p4est_gloidx_t      num_corrected;
+#endif /* P4EST_ENABLE_MPI */
+
+  P4EST_ASSERT (p4est_is_valid (p4est));
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Into " P4EST_STRING
+     "_partition with %lld total quadrants\n",
+     (long long) p4est->global_num_quadrants);
+
+  /* this function does nothing in a serial setup */
+  if (p4est->mpisize == 1) {
+    P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_partition no shipping\n");
+    return global_shipped;
+  }
+
+  p4est_log_indent_push ();
+
+#ifdef P4EST_ENABLE_MPI
+  /* allocate new quadrant distribution counts */
+  num_quadrants_in_proc = P4EST_ALLOC (p4est_locidx_t, num_procs);
+
+  if (weight_fn == NULL) {
+    /* Divide up the quadants equally */
+    for (p = 0, next_quadrant = 0; p < num_procs; ++p) {
+      prev_quadrant = next_quadrant;
+      next_quadrant =
+        p4est_partition_cut_gloidx (global_num_quadrants, p + 1, num_procs);
+      qcount = next_quadrant - prev_quadrant;
+      P4EST_ASSERT (0 <= qcount
+                    && qcount <= (p4est_gloidx_t) P4EST_LOCIDX_MAX);
+      num_quadrants_in_proc[p] = (p4est_locidx_t) (qcount);
+    }
+  }
+  else {
+    /* do a weighted partition */
+    local_weights = P4EST_ALLOC (int64_t, local_num_quadrants + 1);
+    global_weight_sums = P4EST_ALLOC (int64_t, num_procs + 1);
+    P4EST_VERBOSEF ("local quadrant count %lld\n",
+                    (long long) local_num_quadrants);
+
+    /* linearly sum weights across all trees */
+    kl = 0;
+    local_weights[0] = 0;
+    for (nt = first_tree; nt <= last_tree; ++nt) {
+      tree = p4est_tree_array_index (p4est->trees, nt);
+      for (lz = 0; lz < tree->quadrants.elem_count; ++lz, ++kl) {
+        q = p4est_quadrant_array_index (&tree->quadrants, lz);
+        weight = (int64_t) weight_fn (p4est, nt, q);
+        P4EST_ASSERT (weight >= 0);
+        local_weights[kl + 1] = local_weights[kl] + weight;
+      }
+    }
+    P4EST_ASSERT (kl == local_num_quadrants);
+    weight_sum = local_weights[local_num_quadrants];
+    P4EST_VERBOSEF ("local weight sum %lld\n", (long long) weight_sum);
+
+    /* distribute local weight sums */
+    global_weight_sums[0] = 0;
+    mpiret = MPI_Allgather (&weight_sum, 1, MPI_LONG_LONG_INT,
+                            &global_weight_sums[1], 1, MPI_LONG_LONG_INT,
+                            p4est->mpicomm);
+    SC_CHECK_MPI (mpiret);
+
+    /* adjust all arrays to reflect the global weight */
+    for (i = 0; i < num_procs; ++i) {
+      global_weight_sums[i + 1] += global_weight_sums[i];
+    }
+    if (rank > 0) {
+      weight_sum = global_weight_sums[rank];
+      for (kl = 0; kl <= local_num_quadrants; ++kl) {
+        local_weights[kl] += weight_sum;
+      }
+    }
+    P4EST_ASSERT (local_weights[0] == global_weight_sums[rank]);
+    P4EST_ASSERT (local_weights[local_num_quadrants] ==
+                  global_weight_sums[rank + 1]);
+    weight_sum = global_weight_sums[num_procs];
+
+    if (rank == 0) {
+      for (i = 0; i <= num_procs; ++i) {
+        P4EST_GLOBAL_VERBOSEF ("Global weight sum [%d] %lld\n",
+                               i, (long long) global_weight_sums[i]);
+      }
+    }
+
+    /* if all quadrants have zero weight we do nothing */
+    if (weight_sum == 0) {
+      P4EST_FREE (local_weights);
+      P4EST_FREE (global_weight_sums);
+      P4EST_FREE (num_quadrants_in_proc);
+      p4est_log_indent_pop ();
+      P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING
+                               "_partition no shipping\n");
+      return global_shipped;
+    }
+
+    /* determine processor ids to send to */
+    send_lowest = num_procs;
+    send_highest = 0;
+    for (i = 1; i <= num_procs; ++i) {
+      cut = p4est_partition_cut_uint64 (weight_sum, i, num_procs);
+      if (global_weight_sums[rank] < cut &&
+          cut <= global_weight_sums[rank + 1]) {
+        send_lowest = SC_MIN (send_lowest, i);
+        send_highest = SC_MAX (send_highest, i);
+      }
+    }
+    /*
+     * send low cut to send_lowest..send_highest
+     * and high cut to send_lowest-1..send_highest-1
+     */
+    P4EST_LDEBUGF ("my send peers %d %d\n", send_lowest, send_highest);
+
+    num_sends = 2 * (send_highest - send_lowest + 1);
+    if (num_sends <= 0) {
+      num_sends = 0;
+      send_requests = NULL;
+      send_array = NULL;
+    }
+    else {
+      send_requests = P4EST_ALLOC (MPI_Request, num_sends);
+      send_array = P4EST_ALLOC (p4est_gloidx_t, num_sends);
+      lowers = 0;
+      for (i = send_lowest; i <= send_highest; ++i) {
+        base_index = 2 * (i - send_lowest);
+        if (i < num_procs) {
+          /* do binary search in the weight array */
+          lowers = sc_search_lower_bound64 (p4est_partition_cut_uint64
+                                            (weight_sum, i, num_procs),
+                                            local_weights,
+                                            (size_t) local_num_quadrants + 1,
+                                            (size_t) lowers);
+          P4EST_ASSERT (lowers > 0
+                        && (p4est_locidx_t) lowers <= local_num_quadrants);
+
+          /* send low bound */
+          send_index = send_array[base_index + 1] =
+            (p4est_gloidx_t) lowers + p4est->global_first_quadrant[rank];
+          P4EST_LDEBUGF ("send A %d %d %d index %lld base %d to %d\n",
+                         send_lowest, i, send_highest,
+                         (long long) send_index, base_index + 1, i);
+          mpiret =
+            MPI_Isend (&send_array[base_index + 1], 1, P4EST_MPI_GLOIDX, i,
+                       P4EST_COMM_PARTITION_WEIGHTED_LOW, p4est->mpicomm,
+                       &send_requests[base_index + 1]);
+          SC_CHECK_MPI (mpiret);
+        }
+        else {
+          lowers = 0;
+          send_index = global_num_quadrants;
+          send_requests[base_index + 1] = MPI_REQUEST_NULL;
+          send_array[base_index + 1] = -1;
+        }
+
+        /* send high bound */
+        send_array[base_index] = send_index;
+        P4EST_LDEBUGF ("send B %d %d %d index %lld base %d to %d\n",
+                       send_lowest, i, send_highest,
+                       (long long) send_index, base_index, i - 1);
+        mpiret = MPI_Isend (&send_array[base_index], 1, P4EST_MPI_GLOIDX,
+                            i - 1, P4EST_COMM_PARTITION_WEIGHTED_HIGH,
+                            p4est->mpicomm, &send_requests[base_index]);
+        SC_CHECK_MPI (mpiret);
+      }
+    }
+
+    /* determine processor ids to receive from and post irecv */
+    i = 0;
+    my_lowcut = p4est_partition_cut_uint64 (weight_sum, rank, num_procs);
+    if (my_lowcut == 0) {
+      recv_low = 0;
+      recv_requests[0] = MPI_REQUEST_NULL;
+      low_source = -1;
+    }
+    else {
+      for (; i < num_procs; ++i) {
+        if (global_weight_sums[i] < my_lowcut &&
+            my_lowcut <= global_weight_sums[i + 1]) {
+          P4EST_LDEBUGF ("recv A from %d\n", i);
+          mpiret = MPI_Irecv (&recv_low, 1, P4EST_MPI_GLOIDX, i,
+                              P4EST_COMM_PARTITION_WEIGHTED_LOW,
+                              p4est->mpicomm, &recv_requests[0]);
+          SC_CHECK_MPI (mpiret);
+          break;
+        }
+      }
+      P4EST_ASSERT (i < num_procs);
+      low_source = i;
+    }
+    my_highcut = p4est_partition_cut_uint64 (weight_sum, rank + 1, num_procs);
+    if (my_highcut == 0) {
+      recv_high = 0;
+      recv_requests[1] = MPI_REQUEST_NULL;
+      high_source = -1;
+    }
+    else {
+      for (; i < num_procs; ++i) {
+        if (global_weight_sums[i] < my_highcut &&
+            my_highcut <= global_weight_sums[i + 1]) {
+          P4EST_LDEBUGF ("recv B from %d\n", i);
+          mpiret = MPI_Irecv (&recv_high, 1, P4EST_MPI_GLOIDX, i,
+                              P4EST_COMM_PARTITION_WEIGHTED_HIGH,
+                              p4est->mpicomm, &recv_requests[1]);
+          SC_CHECK_MPI (mpiret);
+          break;
+        }
+      }
+      P4EST_ASSERT (i < num_procs);
+      high_source = i;
+    }
+    P4EST_LDEBUGF ("my recv peers %d %d cuts %lld %lld\n",
+                   low_source, high_source,
+                   (long long) my_lowcut, (long long) my_highcut);
+
+    /* free temporary memory */
+    P4EST_FREE (local_weights);
+    P4EST_FREE (global_weight_sums);
+
+    /* wait for sends and receives to complete */
+    if (num_sends > 0) {
+      mpiret = MPI_Waitall (num_sends, send_requests, MPI_STATUSES_IGNORE);
+      SC_CHECK_MPI (mpiret);
+      P4EST_FREE (send_requests);
+      P4EST_FREE (send_array);
+    }
+    mpiret = MPI_Waitall (2, recv_requests, recv_statuses);
+    SC_CHECK_MPI (mpiret);
+    if (my_lowcut != 0) {
+      SC_CHECK_ABORT (recv_statuses[0].MPI_SOURCE == low_source,
+                      "Wait low source");
+      SC_CHECK_ABORT (recv_statuses[0].MPI_TAG ==
+                      P4EST_COMM_PARTITION_WEIGHTED_LOW, "Wait low tag");
+      mpiret = MPI_Get_count (&recv_statuses[0], P4EST_MPI_GLOIDX, &rcount);
+      SC_CHECK_MPI (mpiret);
+      SC_CHECK_ABORTF (rcount == 1, "Wait low count %d", rcount);
+    }
+    if (my_highcut != 0) {
+      SC_CHECK_ABORT (recv_statuses[1].MPI_SOURCE == high_source,
+                      "Wait high source");
+      SC_CHECK_ABORT (recv_statuses[1].MPI_TAG ==
+                      P4EST_COMM_PARTITION_WEIGHTED_HIGH, "Wait high tag");
+      mpiret = MPI_Get_count (&recv_statuses[1], P4EST_MPI_GLOIDX, &rcount);
+      SC_CHECK_MPI (mpiret);
+      SC_CHECK_ABORTF (rcount == 1, "Wait high count %d", rcount);
+    }
+
+    /* communicate the quadrant ranges */
+    qcount = recv_high - recv_low;
+    P4EST_LDEBUGF ("weighted partition count %lld\n", (long long) qcount);
+    P4EST_ASSERT (qcount >= 0 && qcount <= (p4est_gloidx_t) P4EST_LOCIDX_MAX);
+    qlocal = (p4est_locidx_t) qcount;
+    mpiret = MPI_Allgather (&qlocal, 1, P4EST_MPI_LOCIDX,
+                            num_quadrants_in_proc, 1, P4EST_MPI_LOCIDX,
+                            p4est->mpicomm);
+    SC_CHECK_MPI (mpiret);
+
+#if(0)
+    /* run through the count array and repair zero ranges */
+    for (i = 0; i < num_procs; ++i) {
+      if (num_quadrants_in_proc[i] == 0) {
+        for (p = i - 1; p >= 0; --p) {
+          P4EST_ASSERT (num_quadrants_in_proc[p] > 0);
+          if (num_quadrants_in_proc[p] > 1) {
+            --num_quadrants_in_proc[p];
+            ++num_quadrants_in_proc[i];
+            break;
+          }
+        }
+        if (p < 0) {
+          for (p = i + 1; p < num_procs; ++p) {
+            P4EST_ASSERT (num_quadrants_in_proc[p] >= 0);
+            if (num_quadrants_in_proc[p] > 1) {
+              --num_quadrants_in_proc[p];
+              ++num_quadrants_in_proc[i];
+              break;
+            }
+          }
+          P4EST_ASSERT (p < num_procs);
+        }
+      }
+    }
+#endif
+  }
+
+  /* correct partition */
+  if (partition_for_coarsening) {
+    num_corrected =
+      p4est_partition_for_coarsening (p4est, num_quadrants_in_proc);
+    P4EST_GLOBAL_INFOF
+      ("Designated partition for coarsening %lld quadrants moved\n",
+       (long long) num_corrected);
+  }
+
+  /* run the partition algorithm with proper quadrant counts */
+  global_shipped = p4est_partition_given (p4est, num_quadrants_in_proc);
+  P4EST_FREE (num_quadrants_in_proc);
+
+  /* check validity of the p4est */
+  P4EST_ASSERT (p4est_is_valid (p4est));
+#endif /* P4EST_ENABLE_MPI */
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Done " P4EST_STRING "_partition shipped %lld quadrants %.3g%%\n",
+     (long long) global_shipped,
+     global_shipped * 100. / global_num_quadrants);
+
+  return global_shipped;
+}
+
+p4est_gloidx_t
+p4est_partition_for_coarsening (p4est_t * p4est,
+                                p4est_locidx_t * num_quadrants_in_proc)
+{
+#ifdef P4EST_ENABLE_MPI
+  int                 num_procs = p4est->mpisize;
+  int                 rank = p4est->mpirank;
+  int                 mpiret;
+  p4est_gloidx_t      global_num_quadrants = p4est->global_num_quadrants;
+  int                 i, send_lowest, send_highest, num_sends;
+  int                 parent_index;
+  p4est_quadrant_t   *q;
+  p4est_tree_t       *tree;
+  p4est_locidx_t      num_quadrants_in_tree;
+  p4est_topidx_t      it, tree_index;
+  p4est_gloidx_t      iq, quad_id_near_cut;
+  p4est_gloidx_t      min_quad_id, max_quad_id;
+  int8_t              quad_near_cut_level;
+  p4est_gloidx_t     *partition_now = p4est->global_first_quadrant;
+  p4est_gloidx_t     *partition_new;
+  p4est_quadrant_t   *parent_send;
+  MPI_Request        *send_requests;
+  MPI_Request        *receive_requests;
+  int                 receive_lowest, receive_highest, num_receives;
+  int                 process_with_cut = -1, process_with_cut_recv_id = -1;
+  p4est_quadrant_t   *parent_receive;
+  int                *receive_process;
+  int                *correction, correction_local = 0;
+  int                 current_proc, next_proc;
+  p4est_gloidx_t      num_moved_quadrants;
+
+  /* create array with first quadrants of new partition */
+  partition_new = P4EST_ALLOC (p4est_gloidx_t, num_procs + 1);
+  partition_new[0] = 0;
+  for (i = 1; i < num_procs; i++) {     /* loop over all processes */
+    partition_new[i] = partition_new[i - 1] + num_quadrants_in_proc[i - 1];
+  }
+  partition_new[num_procs] = global_num_quadrants;
+
+  /* BEGIN: send */
+  if (partition_now[rank] < partition_now[rank + 1]) {
+    /* if this process has quadrants */
+    /* determine number and min/max process ids to send to */
+    num_sends = 0;              /* number of sends */
+    send_lowest = num_procs;    /* lowest process id */
+    send_highest = 0;           /* highest process id */
+    for (i = 1; i < num_procs; i++) {
+      /* loop over all processes (without first) */
+      if (partition_new[i] < partition_new[i + 1] &&
+          partition_now[rank] <= partition_new[i] + P4EST_CHILDREN - 2 &&
+          partition_new[i] - P4EST_CHILDREN + 1 < partition_now[rank + 1]) {
+        /* if this process has relevant quadrants for process `i` */
+        num_sends++;
+        send_lowest = SC_MIN (send_lowest, i);
+        send_highest = SC_MAX (send_highest, i);
+      }
+    }
+  }
+  else {
+    /* set number of messages to send */
+    num_sends = 0;
+  }
+
+  if (num_sends > 0) {          /* if this process sends messages */
+    /* allocate send messages */
+    send_requests = P4EST_ALLOC (MPI_Request, num_sends);
+    parent_send = P4EST_ALLOC (p4est_quadrant_t, num_sends);
+
+    /* array index of send messages */
+    parent_index = 0;
+
+    for (i = send_lowest; i <= send_highest; i++) {
+      /* loop over all process candidates to send to */
+      if (!(partition_new[i] < partition_new[i + 1] &&
+            partition_now[rank] <= partition_new[i] + P4EST_CHILDREN - 2 &&
+            partition_new[i] - P4EST_CHILDREN + 1 <
+            partition_now[rank + 1])) {
+        /* if this process has no relevant quadrants for process `i` */
+        continue;
+      }
+
+      /* get nearest quadrant `quad_id_near_cut` to cut `partition_new[i]` */
+      if (partition_now[rank] <= partition_new[i] &&
+          partition_new[i] < partition_now[rank + 1]) {
+        /* if cut is owned by this process */
+        quad_id_near_cut = partition_new[i];
+      }
+      else {
+        if (abs (partition_new[i] - partition_now[rank]) <
+            abs (partition_new[i] - partition_now[rank + 1] + 1)) {
+          quad_id_near_cut = partition_now[rank];
+        }
+        else {
+          quad_id_near_cut = partition_now[rank + 1] - 1;
+        }
+      }
+
+      /* get tree `tree` of quadrant `quad_id_near_cut` */
+      num_quadrants_in_tree = partition_now[rank + 1] - partition_now[rank];
+      tree_index = -1;
+      for (it = p4est->first_local_tree; it <= p4est->last_local_tree; it++) {
+        /* loop over all local trees */
+        tree = p4est_tree_array_index (p4est->trees, it);
+        if (tree->quadrants_offset <= quad_id_near_cut - partition_now[rank]) {
+          tree_index = it;
+        }
+        else {
+          num_quadrants_in_tree = tree->quadrants_offset;
+          break;
+        }
+      }
+      tree = p4est_tree_array_index (p4est->trees, tree_index);
+      num_quadrants_in_tree -= tree->quadrants_offset;
+
+      /* get quadrant with index `quad_id_near_cut` */
+      q = p4est_quadrant_array_index (&tree->quadrants,
+                                      quad_id_near_cut - partition_now[rank] -
+                                      tree->quadrants_offset);
+
+      /* get level of quadrant near cut */
+      quad_near_cut_level = q->level;
+
+      if (quad_near_cut_level > 0) {
+        /* if quadrant near cut is not root of tree, i.e. level is not zero */
+        /* get parent of quadrant near cut */
+        p4est_quadrant_parent (q, &parent_send[parent_index]);
+
+        /* get min quadrant with same parent */
+        min_quad_id = quad_id_near_cut;
+        for (iq = quad_id_near_cut;
+             iq >= SC_MAX (partition_now[rank] + tree->quadrants_offset,
+                           partition_new[i] - P4EST_CHILDREN + 1); iq--) {
+          /* loop over eligible quadrants */
+          /* get quadrant with index `iq` */
+          q = p4est_quadrant_array_index (&tree->quadrants,
+                                          iq - partition_now[rank] -
+                                          tree->quadrants_offset);
+
+          /* check quadrant `iq` */
+          if (q->level == quad_near_cut_level) {        /* if same level */
+            if (p4est_quadrant_is_parent (&parent_send[parent_index], q)) {
+              /* if same parent */
+              min_quad_id = iq;
+            }
+            else {
+              break;
+            }
+          }
+          else {
+            break;
+          }
+        }
+
+        /* get max quadrant with same parent */
+        max_quad_id = quad_id_near_cut;
+        for (iq = quad_id_near_cut;
+             iq <=
+             SC_MIN (partition_now[rank] + tree->quadrants_offset +
+                     num_quadrants_in_tree - 1,
+                     partition_new[i] + P4EST_CHILDREN - 2); iq++) {
+          /* loop over eligible quadrants */
+
+          /* get quadrant `iq` */
+          q = p4est_quadrant_array_index (&tree->quadrants,
+                                          iq - partition_now[rank] -
+                                          tree->quadrants_offset);
+
+          /* check quadrant `iq` */
+          if (q->level == quad_near_cut_level) {        /* if same level */
+            if (p4est_quadrant_is_parent (&parent_send[parent_index], q)) {
+              /* if same parent */
+              max_quad_id = iq;
+            }
+            else {
+              break;
+            }
+          }
+          else {
+            break;
+          }
+        }
+
+        /* write tree */
+        parent_send[parent_index].p.piggy3.which_tree = tree_index;
+
+        if (quad_id_near_cut == partition_new[i]) {
+          /* if this process has cut */
+          /* encode number of quadrants with same parent before and after
+           * `partition_new[i]` into one integer */
+          parent_send[parent_index].p.piggy3.local_num =
+            (partition_new[i] - min_quad_id) * P4EST_CHILDREN +
+            (max_quad_id - partition_new[i]);
+        }
+        else {
+          /* write number of quadrants with same parent */
+          parent_send[parent_index].p.piggy3.local_num =
+            max_quad_id - min_quad_id + 1;
+        }
+
+        /* MPI send: parent */
+        mpiret = MPI_Isend (&parent_send[parent_index],
+                            sizeof (p4est_quadrant_t), MPI_BYTE,
+                            i,
+                            P4EST_COMM_PARTITION_CORRECTION, p4est->mpicomm,
+                            &send_requests[parent_index]);
+        SC_CHECK_MPI (mpiret);
+      }
+      else {
+        /* if quadrant near cut is root of tree, i.e., level is zero,
+           set parent as tree root `q` */
+        parent_send[parent_index].level = q->level;
+        parent_send[parent_index].x = q->x;
+        parent_send[parent_index].y = q->y;
+#ifdef P4_TO_P8
+        parent_send[parent_index].z = q->z;
+#endif
+
+        /* write tree */
+        parent_send[parent_index].p.piggy3.which_tree = tree_index;
+
+        /* write number of quadrants with same "parent" */
+        parent_send[parent_index].p.piggy3.local_num = 1;
+
+        /* MPI send: root of tree */
+        mpiret = MPI_Isend (&parent_send[parent_index],
+                            sizeof (p4est_quadrant_t), MPI_BYTE,
+                            i,
+                            P4EST_COMM_PARTITION_CORRECTION, p4est->mpicomm,
+                            &send_requests[parent_index]);
+        SC_CHECK_MPI (mpiret);
+      }
+
+      /* increment parent index */
+      parent_index++;
+    }
+  }
+  /* END: send */
+
+  /* BEGIN: receive */
+  if (rank != 0 && partition_new[rank] < partition_new[rank + 1]) {
+    /* if this process should get quadrants */
+    /* determine process ids to receive from */
+    num_receives = 0;           /* number of receives */
+    receive_lowest = num_procs; /* lowest process id */
+    receive_highest = 0;        /* highest process id */
+    for (i = 0; i < num_procs; i++) {   /* loop over all processes */
+      if (partition_now[i] < partition_now[i + 1] &&
+          partition_now[i] <= partition_new[rank] + P4EST_CHILDREN - 2 &&
+          partition_new[rank] - P4EST_CHILDREN + 1 < partition_now[i + 1]) {
+        /* if process `i` has relevant quadrants for this process */
+        num_receives++;
+        receive_lowest = SC_MIN (receive_lowest, i);
+        receive_highest = SC_MAX (receive_highest, i);
+
+        if (partition_now[i] <= partition_new[rank] &&
+            partition_new[rank] < partition_now[i + 1]) {
+          /* if cut is owned by process `i` */
+          /* process id that sends parent of cut quadrant */
+          process_with_cut = i;
+
+          /* array index of receive messages of process with cut  */
+          process_with_cut_recv_id = num_receives - 1;
+        }
+      }
+    }
+  }
+  else {
+    /* set number of messages to receive */
+    num_receives = 0;
+
+    /* set correction */
+    correction_local = 0;
+  }
+
+  if (num_receives > 0) {       /* if this process receives messages */
+    /* allocate receive messages */
+    receive_requests = P4EST_ALLOC (MPI_Request, num_receives);
+    parent_receive = P4EST_ALLOC (p4est_quadrant_t, num_receives);
+    receive_process = P4EST_ALLOC (int, num_receives);
+
+    /* array index of receive messages */
+    parent_index = 0;
+
+    for (i = receive_lowest; i <= receive_highest; i++) {
+      /* loop over all process candidates to receive from */
+      if (!(partition_now[i] < partition_now[i + 1] &&
+            partition_now[i] <= partition_new[rank] + P4EST_CHILDREN - 2 &&
+            partition_new[rank] - P4EST_CHILDREN + 1 <
+            partition_now[i + 1])) {
+        /* if process `i` has no relevant quadrants for this process */
+        continue;
+      }
+      /* store process index */
+      receive_process[parent_index] = i;
+
+      /* MPI receive */
+      mpiret = MPI_Irecv (&parent_receive[parent_index],
+                          sizeof (p4est_quadrant_t), MPI_BYTE,
+                          i,
+                          P4EST_COMM_PARTITION_CORRECTION, p4est->mpicomm,
+                          &receive_requests[parent_index]);
+      SC_CHECK_MPI (mpiret);
+
+      /* increment parent index */
+      parent_index++;
+    }
+  }
+  /* END: receive */
+
+  /* BEGIN: wait for MPI receive to complete */
+  if (num_receives > 0) {
+    /* wait for receives to complete */
+    mpiret =
+      MPI_Waitall (num_receives, receive_requests, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+
+    /* free receive memory */
+    P4EST_FREE (receive_requests);
+  }
+  /* END: wait for MPI recieve to complete */
+
+  /* BEGIN: compute correction with received quadrants */
+  if (num_receives > 0) {
+    /* if this process received quadrants */
+    min_quad_id = partition_new[rank];  /* min quadrant id with same parent */
+    max_quad_id = partition_new[rank];  /* max quadrant id with same parent */
+
+    for (i = 0; i < num_receives; i++) {
+      /* loop over all received (parent or tree root) quadrants */
+      if (parent_receive[i].p.piggy3.which_tree ==
+          parent_receive[process_with_cut_recv_id].p.piggy3.which_tree
+          &&
+          p4est_quadrant_is_equal (&parent_receive[i],
+                                   &parent_receive[process_with_cut_recv_id]
+          )) {
+        /* if trees and parents are equal */
+        /* decrease/increase min/max quadrant with same parent */
+        if (receive_process[i] < process_with_cut) {
+          /* if before process with cut */
+          /* decrease min quadrant */
+          min_quad_id -= parent_receive[i].p.piggy3.local_num;
+        }
+        else if (receive_process[i] > process_with_cut) {
+          /* if after process with cut */
+          /* increase max quadrant */
+          max_quad_id += parent_receive[i].p.piggy3.local_num;
+        }
+        else {
+          /* decrease min quadrant */
+          min_quad_id -=
+            parent_receive[i].p.piggy3.local_num / P4EST_CHILDREN;
+
+          /* increase max quadrant */
+          max_quad_id +=
+            parent_receive[i].p.piggy3.local_num % P4EST_CHILDREN;
+        }
+      }
+    }
+
+    /* compute correction */
+    correction_local =
+      (int) p4est_partition_correction (partition_new, num_procs, rank,
+                                        min_quad_id, max_quad_id);
+
+    /* free receive memory */
+    P4EST_FREE (parent_receive);
+    P4EST_FREE (receive_process);
+  }
+  /* END: compute correction with received parent quadrants */
+
+  /* free memory */
+  P4EST_FREE (partition_new);
+
+  /* communicate corrections */
+  correction = P4EST_ALLOC (int, num_procs);
+  mpiret = MPI_Allgather (&correction_local, 1, MPI_INT,
+                          correction, 1, MPI_INT, p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  /* BEGIN: wait for MPI send to complete */
+  if (num_sends > 0) {
+    /* wait for sends to complete */
+    mpiret = MPI_Waitall (num_sends, send_requests, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+
+    /* free send memory */
+    P4EST_FREE (parent_send);
+    P4EST_FREE (send_requests);
+  }
+  /* END: wait for MPI send to complete */
+
+  /* correct partition */
+  current_proc =
+    p4est_next_nonempty_process (0, num_procs, num_quadrants_in_proc);
+  next_proc =
+    p4est_next_nonempty_process (current_proc + 1, num_procs,
+                                 num_quadrants_in_proc);
+  num_moved_quadrants = 0;
+  while (current_proc < num_procs) {
+    /* loop over all non empty processes */
+    /* compute correct partition for process `current_proc` */
+    if (0 < current_proc && current_proc < num_procs) {
+      /* if any process but first */
+      num_quadrants_in_proc[current_proc] += correction[current_proc];
+      num_moved_quadrants += (p4est_gloidx_t) abs (correction[current_proc]);
+    }
+    if (next_proc < num_procs) {
+      /* if first process or next process is feasible */
+      num_quadrants_in_proc[current_proc] -= correction[next_proc];
+    }
+
+    /* increase process ids */
+    current_proc = next_proc;
+    next_proc =
+      p4est_next_nonempty_process (next_proc + 1, num_procs,
+                                   num_quadrants_in_proc);
+  }
+
+  /* free memory */
+  P4EST_FREE (correction);
+
+  /* return absolute number of moved quadrants */
+  return num_moved_quadrants;
+#else
+  P4EST_ASSERT (num_quadrants_in_proc[0] == p4est->local_num_quadrants);
+
+  return 0;
+#endif
+}
+
+unsigned
+p4est_checksum (p4est_t * p4est)
+{
+#ifdef P4EST_HAVE_ZLIB
+  uLong               treecrc, crc;
+  size_t              scount, ssum;
+  p4est_topidx_t      nt;
+  p4est_tree_t       *tree;
+  sc_array_t          checkarray;
+
+  P4EST_ASSERT (p4est_is_valid (p4est));
+
+  sc_array_init (&checkarray, 4);
+  crc = adler32 (0, Z_NULL, 0);
+  ssum = 0;
+  for (nt = p4est->first_local_tree; nt <= p4est->last_local_tree; ++nt) {
+    tree = p4est_tree_array_index (p4est->trees, nt);
+    treecrc =
+      (uLong) p4est_quadrant_checksum (&tree->quadrants, &checkarray, 0);
+    scount = 4 * checkarray.elem_count;
+    ssum += scount;
+    crc = adler32_combine (crc, treecrc, (z_off_t) scount);
+  }
+  sc_array_reset (&checkarray);
+  P4EST_ASSERT ((p4est_locidx_t) ssum ==
+                p4est->local_num_quadrants * 4 * (P4EST_DIM + 1));
+
+  return p4est_comm_checksum (p4est, (unsigned) crc, ssum);
+#else
+  sc_abort_collective
+    ("Configure did not find a recent enough zlib.  Abort.\n");
+
+  return 0;
+#endif /* !P4EST_HAVE_ZLIB */
+}
+
+void
+p4est_save (const char *filename, p4est_t * p4est, int save_data)
+{
+  p4est_save_ext (filename, p4est, save_data, 1);
+}
+
+void
+p4est_save_ext (const char *filename, p4est_t * p4est,
+                int save_data, int save_partition)
+{
+  const int           headc = 6;
+  const int           align = 32;
+#ifdef P4EST_ENABLE_MPI
+  int                 mpiret;
+#ifndef P4EST_MPIIO_WRITE
+  MPI_Status          mpistatus;
+#endif
+#endif
+  int                 retval;
+  int                 num_procs, save_num_procs, rank;
+  int                 i;
+  long                fpos = -1, foffset;
+  size_t              data_size, qbuf_size, comb_size, head_count;
+  size_t              zz, zcount;
+  uint64_t           *u64a;
+  FILE               *file;
+#ifdef P4EST_MPIIO_WRITE
+  MPI_File            mpifile;
+  MPI_Offset          mpipos;
+  MPI_Offset          mpithis;
+#else
+  long                fthis;
+#endif
+  p4est_topidx_t      jt, num_trees;
+  p4est_gloidx_t     *pertree;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *q;
+  char               *lbuf, *bp;
+  p4est_qcoord_t     *qpos;
+  sc_array_t         *tquadrants;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING "_save %s\n", filename);
+  p4est_log_indent_push ();
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (p4est->connectivity));
+  P4EST_ASSERT (p4est_is_valid (p4est));
+
+  /* when data is not saved the size is set to zero */
+  data_size = save_data ? p4est->data_size : 0;
+
+  /* zero data size is effectively not saved */
+  if (data_size == 0) {
+    save_data = 0;
+  }
+
+  /* other parameters */
+  num_trees = p4est->connectivity->num_trees;
+  num_procs = p4est->mpisize;
+  save_num_procs = save_partition ? num_procs : 1;
+  head_count = (size_t) (headc + save_num_procs) + (size_t) num_trees;
+  rank = p4est->mpirank;
+  qbuf_size = (P4EST_DIM + 1) * sizeof (p4est_qcoord_t);
+  comb_size = qbuf_size + data_size;
+  pertree = P4EST_ALLOC (p4est_gloidx_t, num_trees + 1);
+  p4est_comm_count_pertree (p4est, pertree);
+
+  if (rank == 0) {
+    p4est_connectivity_save (filename, p4est->connectivity);
+
+    /* open file after writing connectivity to it */
+    file = fopen (filename, "ab");
+    SC_CHECK_ABORT (file != NULL, "file open");
+
+    /* align the start of the header */
+    fpos = ftell (file);
+    SC_CHECK_ABORT (fpos > 0, "first file tell");
+    while (fpos % align != 0) {
+      retval = fputc ('\0', file);
+      SC_CHECK_ABORT (retval == 0, "first file align");
+      ++fpos;
+    }
+
+    /* write format and partition information */
+    u64a = P4EST_ALLOC (uint64_t, head_count);
+    u64a[0] = P4EST_ONDISK_FORMAT;
+    u64a[1] = (uint64_t) sizeof (p4est_qcoord_t);
+    u64a[2] = (uint64_t) sizeof (p4est_quadrant_t);
+    u64a[3] = (uint64_t) data_size;
+    u64a[4] = (uint64_t) save_data;
+    u64a[5] = (uint64_t) save_num_procs;
+    if (save_partition) {
+      P4EST_ASSERT (save_num_procs == num_procs);
+      for (i = 0; i < num_procs; ++i) {
+        u64a[headc + i] = (uint64_t) p4est->global_first_quadrant[i + 1];
+      }
+    }
+    else {
+      P4EST_ASSERT (save_num_procs == 1);
+      u64a[headc] = (uint64_t) p4est->global_first_quadrant[num_procs];
+    }
+    for (jt = 0; jt < num_trees; ++jt) {
+      u64a[headc + save_num_procs + jt] = (uint64_t) pertree[jt + 1];
+    }
+    sc_fwrite (u64a, sizeof (uint64_t), head_count,
+               file, "write header information");
+    P4EST_FREE (u64a);
+    fpos += head_count * sizeof (uint64_t);
+
+    /* align the start of the quadrants */
+    fpos = ftell (file);
+    SC_CHECK_ABORT (fpos > 0, "second file tell");
+    while (fpos % align != 0) {
+      retval = fputc ('\0', file);
+      SC_CHECK_ABORT (retval == 0, "second file align");
+      ++fpos;
+    }
+
+#ifdef P4EST_MPIIO_WRITE
+    /* We will close the sequential access to the file */
+    /* best attempt to flush file to disk */
+    retval = fflush (file);
+    SC_CHECK_ABORT (retval == 0, "file flush");
+#ifdef P4EST_HAVE_FSYNC
+    retval = fsync (fileno (file));
+    SC_CHECK_ABORT (retval == 0, "file fsync");
+#endif
+    retval = fclose (file);
+    SC_CHECK_ABORT (retval == 0, "file close");
+    file = NULL;
+#else
+    /* file is still open for sequential write mode */
+#endif
+  }
+  else {
+    file = NULL;
+  }
+  P4EST_FREE (pertree);
+
+#ifndef P4EST_MPIIO_WRITE
+  if (rank > 0) {
+    /* wait for sequential synchronization */
+#ifdef P4EST_ENABLE_MPI
+    mpiret = MPI_Recv (&fpos, 1, MPI_LONG, rank - 1, P4EST_COMM_SAVE,
+                       p4est->mpicomm, &mpistatus);
+    SC_CHECK_MPI (mpiret);
+#endif
+
+    /* open file after all previous processors have written to it */
+    file = fopen (filename, "rb+");
+    SC_CHECK_ABORT (file != NULL, "file open");
+  }
+#else
+  /* Every core opens the file in append mode */
+  mpiret = MPI_File_open (p4est->mpicomm, (char *) filename,
+                          MPI_MODE_WRONLY | MPI_MODE_APPEND |
+                          MPI_MODE_UNIQUE_OPEN, MPI_INFO_NULL, &mpifile);
+  SC_CHECK_MPI (mpiret);
+  mpiret = MPI_File_get_position (mpifile, &mpipos);
+  SC_CHECK_MPI (mpiret);
+#endif
+
+  if (rank > 0) {
+    /* seek to the beginning of this processor's storage */
+    foffset = (long) (p4est->global_first_quadrant[rank] * comb_size);
+
+#ifndef P4EST_MPIIO_WRITE
+    fthis = fpos + foffset;
+    retval = fseek (file, fthis, SEEK_SET);
+    SC_CHECK_ABORT (retval == 0, "seek data");
+#else
+    mpithis = mpipos + (MPI_Offset) foffset;
+    mpiret = MPI_File_seek (mpifile, mpithis, MPI_SEEK_SET);
+    SC_CHECK_MPI (mpiret);
+#endif
+  }
+
+  /* write quadrant coordinates and data interleaved */
+  for (jt = p4est->first_local_tree; jt <= p4est->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    tquadrants = &tree->quadrants;
+    zcount = tquadrants->elem_count;
+
+    /* storage that will be written for this tree */
+    bp = lbuf = P4EST_ALLOC (char, comb_size * zcount);
+    for (zz = 0; zz < zcount; ++zz) {
+      qpos = (p4est_locidx_t *) bp;
+      q = p4est_quadrant_array_index (tquadrants, zz);
+      *qpos++ = q->x;
+      *qpos++ = q->y;
+#ifdef P4_TO_P8
+      *qpos++ = q->z;
+#endif
+      *qpos++ = (p4est_qcoord_t) q->level;
+      if (save_data) {
+        memcpy (qpos, q->p.user_data, data_size);
+      }
+      bp += comb_size;
+    }
+#ifndef P4EST_MPIIO_WRITE
+    sc_fwrite (lbuf, comb_size, zcount, file, "write quadrants");
+#else
+    sc_mpi_write (mpifile, lbuf, comb_size * zcount, MPI_BYTE,
+                  "write quadrants");
+#endif
+    P4EST_FREE (lbuf);
+  }
+
+#ifndef P4EST_MPIIO_WRITE
+  /* best attempt to flush file to disk */
+  retval = fflush (file);
+  SC_CHECK_ABORT (retval == 0, "file flush");
+#ifdef P4EST_HAVE_FSYNC
+  retval = fsync (fileno (file));
+  SC_CHECK_ABORT (retval == 0, "file fsync");
+#endif
+  retval = fclose (file);
+  SC_CHECK_ABORT (retval == 0, "file close");
+  file = NULL;
+
+  /* initiate sequential synchronization */
+#ifdef P4EST_ENABLE_MPI
+  if (rank < num_procs - 1) {
+    mpiret = MPI_Send (&fpos, 1, MPI_LONG, rank + 1, P4EST_COMM_SAVE,
+                       p4est->mpicomm);
+    SC_CHECK_MPI (mpiret);
+  }
+#endif
+#else
+  mpiret = MPI_File_close (&mpifile);
+  SC_CHECK_MPI (mpiret);
+#endif
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_save\n");
+}
+
+p4est_t            *
+p4est_load (const char *filename, sc_MPI_Comm mpicomm, size_t data_size,
+            int load_data, void *user_pointer,
+            p4est_connectivity_t ** connectivity)
+{
+  return p4est_load_ext (filename, mpicomm, data_size, load_data,
+                         0, 0, user_pointer, connectivity);
+}
+
+p4est_t            *
+p4est_load_ext (const char *filename, sc_MPI_Comm mpicomm, size_t data_size,
+                int load_data, int autopartition, int broadcasthead,
+                void *user_pointer, p4est_connectivity_t ** connectivity)
+{
+  int                 retval;
+  p4est_t            *p4est;
+  sc_io_source_t     *src;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING "_load %s\n", filename);
+  p4est_log_indent_push ();
+
+  /* open file on all processors */
+
+  src = sc_io_source_new (SC_IO_TYPE_FILENAME, SC_IO_ENCODE_NONE, filename);
+  SC_CHECK_ABORT (src != NULL, "file source: possibly file not found");
+
+  p4est = p4est_source_ext (src, mpicomm, data_size, load_data, autopartition,
+                            broadcasthead, user_pointer, connectivity);
+
+  retval = sc_io_source_destroy (src);
+  SC_CHECK_ABORT (!retval, "source destroy");
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF
+    ("Done " P4EST_STRING "_load with %lld total quadrants\n",
+     (long long) p4est->global_num_quadrants);
+
+  return p4est;
+}
+
+p4est_t            *
+p4est_source_ext (sc_io_source_t * src, sc_MPI_Comm mpicomm, size_t data_size,
+                  int load_data, int autopartition, int broadcasthead,
+                  void *user_pointer, p4est_connectivity_t ** connectivity)
+{
+  const int           headc = 6;
+  const int           align = 32;
+  int                 retval;
+  int                 mpiret;
+  int                 num_procs, rank;
+  int                 save_num_procs;
+  int                 save_data;
+  int                 i;
+  uint64_t           *u64a, u64int;
+  size_t              save_data_size;
+  size_t              qbuf_size, comb_size, head_count;
+  size_t              zz, zcount, zpadding;
+  p4est_topidx_t      jt, num_trees;
+  p4est_gloidx_t     *gfq;
+  p4est_gloidx_t     *pertree;
+  p4est_qcoord_t     *qap;
+  p4est_connectivity_t *conn;
+  p4est_t            *p4est;
+  sc_array_t         *qarr, *darr;
+  char               *dap, *lbuf;
+
+  SC_CHECK_ABORT (!broadcasthead, "Header broadcast not implemented");
+
+  /* retrieve MPI information */
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  /* read connectivity */
+  conn = *connectivity = p4est_connectivity_source (src);
+  SC_CHECK_ABORT (conn != NULL, "connectivity source");
+  zcount = src->bytes_out;
+  zpadding = (align - zcount % align) % align;
+  retval = sc_io_source_read (src, NULL, zpadding, NULL);
+  SC_CHECK_ABORT (!retval, "source padding");
+
+  /* set some parameters */
+  if (data_size == 0) {
+    load_data = 0;
+  }
+  num_trees = conn->num_trees;
+  qbuf_size = (P4EST_DIM + 1) * sizeof (p4est_qcoord_t);
+
+  /* read format and partition information */
+  u64a = P4EST_ALLOC (uint64_t, headc);
+  retval = sc_io_source_read (src, u64a, sizeof (uint64_t) * (size_t) headc,
+                              NULL);
+  SC_CHECK_ABORT (!retval, "read format");
+  SC_CHECK_ABORT (u64a[0] == P4EST_ONDISK_FORMAT, "invalid format");
+  SC_CHECK_ABORT (u64a[1] == (uint64_t) sizeof (p4est_qcoord_t),
+                  "invalid coordinate size");
+  SC_CHECK_ABORT (u64a[2] == (uint64_t) sizeof (p4est_quadrant_t),
+                  "invalid quadrant size");
+  save_data_size = (size_t) u64a[3];
+  save_data = (int) u64a[4];
+  if (load_data) {
+    SC_CHECK_ABORT (save_data_size == data_size, "invalid data size");
+    SC_CHECK_ABORT (save_data, "quadrant data not saved");
+  }
+  save_num_procs = (int) u64a[5];
+  comb_size = qbuf_size + save_data_size;
+  SC_CHECK_ABORT (autopartition || num_procs == save_num_procs,
+                  "num procs mismatch");
+
+  /* create partition data */
+  gfq = P4EST_ALLOC (p4est_gloidx_t, num_procs + 1);
+  if (!autopartition) {
+    P4EST_ASSERT (num_procs == save_num_procs);
+    u64a = P4EST_REALLOC (u64a, uint64_t, num_procs);
+    sc_io_source_read (src, u64a, sizeof (uint64_t) * (size_t) num_procs,
+                       NULL);
+    SC_CHECK_ABORT (!retval, "read quadrant partition");
+    gfq[0] = 0;
+    for (i = 0; i < num_procs; ++i) {
+      gfq[i + 1] = (p4est_gloidx_t) u64a[i];
+    }
+  }
+  else {
+    /* ignore saved partition and compute a new uniform one */
+    retval = sc_io_source_read
+      (src, NULL, (long) ((save_num_procs - 1) * sizeof (uint64_t)), NULL);
+    SC_CHECK_ABORT (!retval, "seek over ignored partition");
+    retval = sc_io_source_read (src, &u64int, sizeof (uint64_t), NULL);
+    SC_CHECK_ABORT (!retval, "read quadrant count");
+    for (i = 0; i <= num_procs; ++i) {
+      gfq[i] = p4est_partition_cut_uint64 (u64int, i, num_procs);
+    }
+  }
+  zcount = (size_t) (gfq[rank + 1] - gfq[rank]);
+
+  /* read pertree data */
+  u64a = P4EST_REALLOC (u64a, uint64_t, num_trees);
+  retval = sc_io_source_read (src, u64a, sizeof (uint64_t) * (size_t)
+                              num_trees, NULL);
+  SC_CHECK_ABORT (!retval, "read pertree information");
+  pertree = P4EST_ALLOC (p4est_gloidx_t, num_trees + 1);
+  pertree[0] = 0;
+  for (jt = 0; jt < num_trees; ++jt) {
+    pertree[jt + 1] = (p4est_gloidx_t) u64a[jt];
+  }
+  SC_CHECK_ABORT (gfq[num_procs] == pertree[num_trees], "pertree mismatch");
+  P4EST_FREE (u64a);
+
+  /* seek to the beginning of this processor's storage */
+  head_count = (size_t) (headc + save_num_procs) + (size_t) num_trees;
+  zpadding = (align - (head_count * sizeof (uint64_t)) % align) % align;
+  if (zpadding > 0 || rank > 0) {
+    retval =
+      sc_io_source_read (src, NULL, (long) (zpadding + gfq[rank] * comb_size),
+                         NULL);
+    SC_CHECK_ABORT (!retval, "seek data");
+  }
+
+  /* read quadrant coordinates and data interleaved */
+  qarr =
+    sc_array_new_size (sizeof (p4est_qcoord_t), (P4EST_DIM + 1) * zcount);
+  qap = (p4est_qcoord_t *) qarr->array;
+  darr = NULL;
+  dap = NULL;
+  lbuf = NULL;
+  if (load_data) {
+    P4EST_ASSERT (data_size == save_data_size && data_size > 0);
+    darr = sc_array_new_size (data_size, zcount);
+    dap = darr->array;
+    lbuf = P4EST_ALLOC (char, comb_size);
+  }
+  for (zz = 0; zz < zcount; ++zz) {
+    if (load_data) {
+      retval = sc_io_source_read (src, lbuf, comb_size, NULL);
+      SC_CHECK_ABORT (!retval, "read quadrant with data");
+      memcpy (qap, lbuf, qbuf_size);
+      memcpy (dap, lbuf + qbuf_size, data_size);
+    }
+    else {
+      retval = sc_io_source_read (src, qap, qbuf_size, NULL);
+      SC_CHECK_ABORT (!retval, "read quadrant with data");
+      if (save_data_size > 0) {
+        retval = sc_io_source_read (src, NULL, save_data_size, NULL);
+        SC_CHECK_ABORT (!retval, "seek over data");
+      }
+    }
+    qap += P4EST_DIM + 1;
+    dap += data_size;
+  }
+  P4EST_FREE (lbuf);
+
+  /* seek every process to the end of the source (in case there is data
+   * appended to the end of this source) */
+  if (gfq[num_procs] > gfq[rank + 1]) {
+    retval = sc_io_source_read
+      (src, NULL, (long) (gfq[num_procs] - gfq[rank + 1]) * comb_size, NULL);
+    SC_CHECK_ABORT (!retval, "seek to end of data");
+  }
+
+  /* create p4est from accumulated information */
+  p4est = p4est_inflate (mpicomm, conn, gfq, pertree,
+                         qarr, darr, user_pointer);
+  sc_array_destroy (qarr);
+  if (darr != NULL) {
+    sc_array_destroy (darr);
+  }
+  P4EST_FREE (pertree);
+  P4EST_FREE (gfq);
+
+  /* assert that we loaded a valid forest and return */
+  SC_CHECK_ABORT (p4est_is_valid (p4est), "invalid forest");
+
+  return p4est;
+}
diff --git a/src/p4est.h b/src/p4est.h
new file mode 100644
index 0000000..32cc000
--- /dev/null
+++ b/src/p4est.h
@@ -0,0 +1,473 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est.h
+ *
+ * The top-level 2D p4est interface.
+ *
+ * \ingroup p4est
+ */
+
+/** \defgroup p4est p4est
+ *
+ * The 2D version of the p4est library.
+ */
+
+#ifndef P4EST_H
+#define P4EST_H
+
+#ifdef P4EST_TO_P8EST_H
+#error "The include files p4est.h and p4est_to_p8est.h cannot be combined"
+#endif
+
+/* p4est_connectivity.h includes p4est_base.h sc_containers.h */
+#include <p4est_connectivity.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** The finest level of the quadtree for representing nodes */
+#define P4EST_MAXLEVEL 30
+
+/** The finest level of the quadtree for representing quadrants */
+#define P4EST_QMAXLEVEL 29
+
+/** The length of a side of the root quadrant */
+#define P4EST_ROOT_LEN ((p4est_qcoord_t) 1 << P4EST_MAXLEVEL)
+
+/** The length of a quadrant of level l */
+#define P4EST_QUADRANT_LEN(l) ((p4est_qcoord_t) 1 << (P4EST_MAXLEVEL - (l)))
+
+/** The offset of the highest (farthest from the origin) quadrant at level l
+ */
+#define P4EST_LAST_OFFSET(l) (P4EST_ROOT_LEN - P4EST_QUADRANT_LEN (l))
+
+/** The 2D quadrant datatype */
+typedef struct p4est_quadrant
+{
+  /*@{*/
+  p4est_qcoord_t      x, y;  /**< coordinates */
+  /*@}*/
+  int8_t              level, /**< level of refinement */
+                      pad8;  /**< padding */
+  int16_t             pad16; /**< padding */
+  union p4est_quadrant_data
+  {
+    void               *user_data;      /**< never changed by p4est */
+    long                user_long;      /**< never changed by p4est */
+    int                 user_int;       /**< never changed by p4est */
+    p4est_topidx_t      which_tree;     /**< the tree containing the quadrant
+                                             (used in auxiliary octants such
+                                             as the ghost octants in
+                                             p4est_ghost_t) */
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      int                 owner_rank;
+    }
+    piggy1; /**< of ghost octants, store the tree and owner rank */
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_topidx_t      from_tree;
+    }
+    piggy2; /**< of transformed octants, store the original tree and the
+                 target tree */
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      local_num;
+    }
+    piggy3; /**< of ghost octants, store the tree and index in the owner's
+                 numbering */
+  }
+  p; /**< a union of additional data attached to a quadrant */
+}
+p4est_quadrant_t;
+
+/** The p4est tree datatype */
+typedef struct p4est_tree
+{
+  sc_array_t          quadrants;             /**< locally stored quadrants */
+  p4est_quadrant_t    first_desc,            /**< first local descendant */
+                      last_desc;             /**< last local descendant */
+  p4est_locidx_t      quadrants_offset;      /**< cumulative sum over earlier
+                                                  trees on this processor
+                                                  (locals only) */
+  p4est_locidx_t      quadrants_per_level[P4EST_MAXLEVEL + 1];
+                                             /**< locals only */
+  int8_t              maxlevel;              /**< highest local quadrant level */
+}
+p4est_tree_t;
+
+/** Data pertaining to selecting, inspecting, and profiling algorithms.
+ * A pointer to this structure is hooked into the p4est main structure.
+ * Declared in p4est_extended.h.  Used to profile important algorithms.
+ */
+typedef struct p4est_inspect p4est_inspect_t;
+
+/** The p4est forest datatype */
+typedef struct p4est
+{
+  sc_MPI_Comm         mpicomm;          /**< MPI communicator */
+  int                 mpisize,          /**< number of MPI processes */
+                      mpirank;          /**< this process's MPI rank */
+  size_t              data_size;        /**< size of per-quadrant p.user_data
+                     (see p4est_quadrant_t::p4est_quadrant_data::user_data) */
+  void               *user_pointer;     /**< convenience pointer for users,
+                                             never touched by p4est */
+
+  p4est_topidx_t      first_local_tree; /**< 0-based index of first local
+                                             tree, must be -1 for an empty
+                                             processor */
+  p4est_topidx_t      last_local_tree;  /**< 0-based index of last local
+                                             tree, must be -2 for an empty
+                                             processor */
+  p4est_locidx_t      local_num_quadrants;   /**< number of quadrants on all
+                                                  trees on this processor */
+  p4est_gloidx_t      global_num_quadrants;  /**< number of quadrants on all
+                                                  trees on all processors */
+  p4est_gloidx_t     *global_first_quadrant; /**< first global quadrant index
+                                                  for each process and 1
+                                                  beyond */
+  p4est_quadrant_t   *global_first_position; /**< first smallest possible quad
+                                                  for each process and 1
+                                                  beyond */
+  p4est_connectivity_t *connectivity; /**< connectivity structure, not owned */
+  sc_array_t         *trees;          /**< array of all trees */
+
+  sc_mempool_t       *user_data_pool; /**< memory allocator for user data */
+                                      /*   WARNING: This is NULL if data size
+                                                    equals zero. */
+  sc_mempool_t       *quadrant_pool;  /**< memory allocator for temporary
+                                           quadrants */
+  p4est_inspect_t    *inspect;        /**< algorithmic switches */
+}
+p4est_t;
+
+/** Calculate memory usage of a forest structure.
+ * The connectivity structure is not counted since it is not owned;
+ * use p4est_connectivity_memory_usage (p4est->connectivity).
+ * \param [in] p4est    Forest structure.
+ * \return              Memory used in bytes.
+ */
+size_t              p4est_memory_used (p4est_t * p4est);
+
+/** Callback function prototype to initialize the quadrant's user data.
+ * \param [in] p4est         the forest
+ * \param [in] which_tree    the tree containing \a quadrant
+ * \param [in,out] quadrant  the quadrant to be initialized: if data_size > 0,
+ *                           the data to be initialized is at
+ *                           \a quadrant->p.user_data; otherwise, the
+ *                           non-pointer user data (such as
+ *                           \a quadrant->p.user_int) can be initialized
+ */
+typedef void        (*p4est_init_t) (p4est_t * p4est,
+                                     p4est_topidx_t which_tree,
+                                     p4est_quadrant_t * quadrant);
+
+/** Callback function prototype to decide for refinement.
+ * \param [in] p4est       the forest
+ * \param [in] which_tree  the tree containing \a quadrant
+ * \param [in] quadrant    the quadrant that may be refined
+ * \return nonzero if the quadrant shall be refined.
+ */
+typedef int         (*p4est_refine_t) (p4est_t * p4est,
+                                       p4est_topidx_t which_tree,
+                                       p4est_quadrant_t * quadrant);
+
+/** Callback function prototype to decide for coarsening.
+ * \param [in] p4est       the forest
+ * \param [in] which_tree  the tree containing \a quadrant
+ * \param [in] quadrants   Pointers to 4 siblings in Morton ordering.
+ * \return nonzero if the quadrants shall be replaced with their parent.
+ */
+typedef int         (*p4est_coarsen_t) (p4est_t * p4est,
+                                        p4est_topidx_t which_tree,
+                                        p4est_quadrant_t * quadrants[]);
+
+/** Callback function prototype to calculate weights for partitioning.
+ * \param [in] p4est       the forest
+ * \param [in] which_tree  the tree containing \a quadrant
+ * \return a 32bit integer >= 0 as the quadrant weight.
+ * \note    Global sum of weights must fit into a 64bit integer.
+ */
+typedef int         (*p4est_weight_t) (p4est_t * p4est,
+                                       p4est_topidx_t which_tree,
+                                       p4est_quadrant_t * quadrant);
+
+extern void        *P4EST_DATA_UNINITIALIZED;
+
+/** set statically allocated quadrant to defined values */
+#define P4EST_QUADRANT_INIT(q) \
+  ((void) memset ((q), -1, sizeof (p4est_quadrant_t)))
+
+/** Transform a quadrant coordinate into the space spanned by tree vertices.
+ * \param [in] connectivity     Connectivity must provide the vertices.
+ * \param [in] treeid           Identify the tree that contains x, y.
+ * \param [in] x, y             Quadrant coordinates relative to treeid.
+ * \param [out] vxy             Transformed coordinates in vertex space.
+ */
+void                p4est_qcoord_to_vertex (p4est_connectivity_t *
+                                            connectivity,
+                                            p4est_topidx_t treeid,
+                                            p4est_qcoord_t x,
+                                            p4est_qcoord_t y, double vxyz[3]);
+
+/** Create a new forest.
+ * The new forest consists of equi-partitioned root quadrants.
+ * When there are more processors than trees, some processors are empty.
+ *
+ * \param [in] mpicomm       A valid MPI communicator.
+ * \param [in] connectivity  This is the connectivity information that
+ *                           the forest is built with.  Note the p4est
+ *                           does not take ownership of the memory.
+ * \param [in] data_size     This is the size of data for each quadrant which
+ *                           can be zero.  Then user_data_pool is set to NULL.
+ * \param [in] init_fn       Callback function to initialize the user_data
+ *                           which is already allocated automatically.
+ * \param [in] user_pointer  Assign to the user_pointer member of the p4est
+ *                           before init_fn is called the first time.
+ *
+ * \return This returns a valid forest.
+ *
+ * \note The connectivity structure must not be destroyed
+ *       during the lifetime of this forest.
+ */
+p4est_t            *p4est_new (sc_MPI_Comm mpicomm,
+                               p4est_connectivity_t * connectivity,
+                               size_t data_size,
+                               p4est_init_t init_fn, void *user_pointer);
+
+/** Destroy a p4est.
+ *
+ * \note The connectivity structure is not destroyed with the p4est.
+ */
+void                p4est_destroy (p4est_t * p4est);
+
+/** Make a deep copy of a p4est.
+ * The connectivity is not duplicated.
+ * Copying of quadrant user data is optional.
+ * If old and new data sizes are 0, the user_data field is copied regardless.
+ * The inspect member of the copy is set to NULL.
+ *
+ * \param [in]  copy_data  If true, data are copied.
+ *                         If false, data_size is set to 0.
+ * \return  Returns a valid p4est that does not depend on the input.
+ */
+p4est_t            *p4est_copy (p4est_t * input, int copy_data);
+
+/** Reset user pointer and element data.
+ * When the data size is changed the quadrant data is freed and allocated.
+ * The initialization callback is invoked on each quadrant.
+ * Old user_data content is disregarded.
+ *
+ * \param [in] data_size     This is the size of data for each quadrant which
+ *                           can be zero.  Then user_data_pool is set to NULL.
+ * \param [in] init_fn       Callback function to initialize the user_data
+ *                           which is already allocated automatically.
+ *                           May be NULL.
+ * \param [in] user_pointer  Assign to the user_pointer member of the p4est
+ *                           before init_fn is called the first time.
+ */
+void                p4est_reset_data (p4est_t * p4est, size_t data_size,
+                                      p4est_init_t init_fn,
+                                      void *user_pointer);
+
+/** Refine a forest.
+ * \param [in,out] p4est The forest is changed in place.
+ * \param [in] refine_recursive Boolean to decide on recursive refinement.
+ * \param [in] refine_fn Callback function that must return true if a quadrant
+ *                       shall be refined.  If refine_recursive is true,
+ *                       refine_fn is called for every existing and newly
+ *                       created quadrant.  Otherwise, it is called for every
+ *                       existing quadrant.  It is possible that a refinement
+ *                       request made by the callback is ignored.  To catch
+ *                       this case, you can examine whether init_fn gets
+ *                       called, or use p4est_refine_ext in p4est_extended.h
+ *                       and examine whether replace_fn gets called.
+ * \param [in] init_fn   Callback function to initialize the user_data of newly
+ *                       created quadrants, which is already allocated.  This
+ *                       function pointer may be NULL.
+ */
+void                p4est_refine (p4est_t * p4est,
+                                  int refine_recursive,
+                                  p4est_refine_t refine_fn,
+                                  p4est_init_t init_fn);
+
+/** Coarsen a forest.
+ * \param [in,out] p4est  The forest is changed in place.
+ * \param [in] coarsen_recursive Boolean to decide on recursive coarsening.
+ * \param [in] coarsen_fn Callback function that returns true if a
+ *                        family of quadrants shall be coarsened
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ */
+void                p4est_coarsen (p4est_t * p4est,
+                                   int coarsen_recursive,
+                                   p4est_coarsen_t coarsen_fn,
+                                   p4est_init_t init_fn);
+
+/** 2:1 balance the size differences of neighboring elements in a forest.
+ * \param [in,out] p4est  The p4est to be worked on.
+ * \param [in] btype      Balance type (face or corner/full).
+ *                        Corner balance is almost never required when
+ *                        discretizing a PDE; just causes smoother mesh grading.
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ */
+void                p4est_balance (p4est_t * p4est,
+                                   p4est_connect_type_t btype,
+                                   p4est_init_t init_fn);
+
+/** Equally partition the forest.
+ * The partition can be by element count or by a user-defined weight.
+ *
+ * The forest will be partitioned between processors such that they
+ * have an approximately equal number of quadrants (or sum of weights).
+ *
+ * \param [in,out] p4est      The forest that will be partitioned.
+ * \param [in]     allow_for_coarsening Slightly modify partition such that
+ *                            quadrant families are not split between ranks.
+ * \param [in]     weight_fn  A weighting function or NULL
+ *                            for uniform partitioning.
+ */
+void                p4est_partition (p4est_t * p4est,
+                                     int allow_for_coarsening,
+                                     p4est_weight_t weight_fn);
+
+/** Compute the checksum for a forest.
+ * Based on quadrant arrays only. It is independent of partition and mpisize.
+ * \return  Returns the checksum on processor 0 only. 0 on other processors.
+ */
+unsigned            p4est_checksum (p4est_t * p4est);
+
+/** Save the complete connectivity/p4est data to disk.
+ *
+ * This is a collective operation that all MPI processes need to call.  All
+ * processes write into the same file, so the filename given needs to be
+ * identical over all parallel invocations.
+ *
+ * By default, we write the current processor count and partition into the file
+ * header.  This makes the file depend on mpisize.  For changing this see
+ * p4est_save_ext() in p4est_extended.h.
+ *
+ * \param [in] filename    Name of the file to write.
+ * \param [in] p4est       Valid forest structure.
+ * \param [in] save_data   If true, the element data is saved.
+ *                         Otherwise, a data size of 0 is saved.
+ * \note            Aborts on file errors.
+ * \note            If p4est is not configured to use MPI-IO, some processes
+ *                  return from this function before the file is complete, in
+ *                  which case immediate read-access to the file may require a
+ *                  call to sc_MPI_Barrier.
+ */
+void                p4est_save (const char *filename, p4est_t * p4est,
+                                int save_data);
+
+/** Load the complete connectivity/p4est structure from disk.
+ *
+ * This is a collective operation that all MPI processes need to call.  All
+ * processes read from the same file, so the filename given needs to be
+ * identical over all parallel invocations.
+ *
+ * By default, a file can only be loaded with the same number of processors
+ * that it was stored with.  The defaults can be changed with p4est_load_ext()
+ * in p4est_extended.h.
+ *
+ * \param [in] filename         Name of the file to read.
+ * \param [in] mpicomm          A valid MPI communicator.
+ * \param [in] data_size        Size of data for each quadrant which can be
+ *                              zero.  Then user_data_pool is set to NULL.
+ *                              If data_size is zero, load_data is ignored.
+ * \param [in] load_data        If true, the element data is loaded.  This is
+ *                              only permitted if the saved data size matches.
+ *                              If false, the stored data size is ignored.
+ * \param [in] user_pointer     Assign to the user_pointer member of the p4est
+ *                              before init_fn is called the first time.
+ * \param [out] connectivity    Connectivity must be destroyed separately.
+ * \return          Returns a valid forest structure. A pointer to a valid
+ *                  connectivity structure is returned through the last
+ *                  argument.
+ * \note            Aborts on file errors or invalid file contents.
+ */
+p4est_t            *p4est_load (const char *filename, sc_MPI_Comm mpicomm,
+                                size_t data_size, int load_data,
+                                void *user_pointer,
+                                p4est_connectivity_t ** connectivity);
+
+/** Return a pointer to an array element indexed by a p4est_topidx_t.
+ * \param [in] index needs to be in [0]..[elem_count-1].
+ */
+/*@unused@*/
+static inline p4est_tree_t *
+p4est_tree_array_index (sc_array_t * array, p4est_topidx_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_tree_t));
+  P4EST_ASSERT (it >= 0 && (size_t) it < array->elem_count);
+
+  return (p4est_tree_t *) (array->array +
+                           sizeof (p4est_tree_t) * (size_t) it);
+}
+
+/** Return a pointer to a quadrant array element indexed by a size_t. */
+/*@unused@*/
+static inline p4est_quadrant_t *
+p4est_quadrant_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_quadrant_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p4est_quadrant_t *) (array->array + sizeof (p4est_quadrant_t) * it);
+}
+
+/** Call sc_array_push for a quadrant array. */
+/*@unused@*/
+static inline p4est_quadrant_t *
+p4est_quadrant_array_push (sc_array_t * array)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_quadrant_t));
+
+  return (p4est_quadrant_t *) sc_array_push (array);
+}
+
+/** Call sc_mempool_alloc for a mempool creating quadrants. */
+/*@unused@*/
+static inline p4est_quadrant_t *
+p4est_quadrant_mempool_alloc (sc_mempool_t * mempool)
+{
+  P4EST_ASSERT (mempool->elem_size == sizeof (p4est_quadrant_t));
+
+  return (p4est_quadrant_t *) sc_mempool_alloc (mempool);
+}
+
+/** Call sc_list pop for a quadrant array. */
+/*@unused@*/
+static inline p4est_quadrant_t *
+p4est_quadrant_list_pop (sc_list_t * list)
+{
+  return (p4est_quadrant_t *) sc_list_pop (list);
+}
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_H */
diff --git a/src/p4est_algorithms.c b/src/p4est_algorithms.c
new file mode 100644
index 0000000..03ec870
--- /dev/null
+++ b/src/p4est_algorithms.c
@@ -0,0 +1,3378 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef P4_TO_P8
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_communication.h>
+#include <p8est_search.h>
+#include <p8est_balance.h>
+#else
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_communication.h>
+#include <p4est_search.h>
+#include <p4est_balance.h>
+#endif /* !P4_TO_P8 */
+
+/* htonl is in either of these two */
+#ifdef P4EST_HAVE_ARPA_NET_H
+#include <arpa/inet.h>
+#endif
+#ifdef P4EST_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifndef P4_TO_P8
+
+/* *INDENT-OFF* */
+
+/** Store the number of quadrants to add for complete and balance stages. */
+static const int    p4est_balance_count[P4EST_DIM + 1] =
+{ 5, 7, 8 };
+
+/** Store coordinates of quadrants to add for balancing. */
+static const p4est_qcoord_t p4est_balance_coord[8][P4EST_DIM] =
+{ /* faces */
+  { -1,  1 },
+  {  2,  0 },
+  {  1, -1 },
+  {  0,  2 },
+  /* corners */
+  { -1, -1 },
+  {  2, -1 },
+  { -1,  2 },
+  {  2,  2 }};
+
+/** Offset for corners into p4est_balance_coord */
+static const int    pbco = P4EST_FACES;
+
+/* *INDENT-ON* */
+
+#endif /* !P4_TO_P8 */
+
+void
+p4est_quadrant_init_data (p4est_t * p4est, p4est_topidx_t which_tree,
+                          p4est_quadrant_t * quad, p4est_init_t init_fn)
+{
+  P4EST_ASSERT (p4est_quadrant_is_extended (quad));
+
+  if (p4est->data_size > 0) {
+    quad->p.user_data = sc_mempool_alloc (p4est->user_data_pool);
+  }
+  else {
+    quad->p.user_data = NULL;
+  }
+  if (init_fn != NULL && p4est_quadrant_is_inside_root (quad)) {
+    init_fn (p4est, which_tree, quad);
+  }
+}
+
+void
+p4est_quadrant_free_data (p4est_t * p4est, p4est_quadrant_t * quad)
+{
+  P4EST_ASSERT (p4est_quadrant_is_extended (quad));
+
+  if (p4est->data_size > 0) {
+    sc_mempool_free (p4est->user_data_pool, quad->p.user_data);
+  }
+  quad->p.user_data = NULL;
+}
+
+unsigned
+p4est_quadrant_checksum (sc_array_t * quadrants,
+                         sc_array_t * checkarray, size_t first_quadrant)
+{
+  int                 own_check;
+  size_t              kz, qcount;
+  unsigned            crc;
+  uint32_t           *check;
+  p4est_quadrant_t   *q;
+
+  qcount = quadrants->elem_count;
+
+  P4EST_ASSERT (quadrants->elem_size == sizeof (p4est_quadrant_t));
+  P4EST_ASSERT (first_quadrant <= qcount);
+
+  if (checkarray == NULL) {
+    checkarray = sc_array_new (4);
+    own_check = 1;
+  }
+  else {
+    P4EST_ASSERT (checkarray->elem_size == 4);
+    own_check = 0;
+  }
+
+  sc_array_resize (checkarray, (qcount - first_quadrant) * (P4EST_DIM + 1));
+  for (kz = first_quadrant; kz < qcount; ++kz) {
+    q = p4est_quadrant_array_index (quadrants, kz);
+    P4EST_ASSERT (p4est_quadrant_is_extended (q));
+    check =
+      (uint32_t *) sc_array_index (checkarray,
+                                   (kz - first_quadrant) * (P4EST_DIM + 1));
+    check[0] = htonl ((uint32_t) q->x);
+    check[1] = htonl ((uint32_t) q->y);
+#ifdef P4_TO_P8
+    check[2] = htonl ((uint32_t) q->z);
+#endif
+    check[P4EST_DIM] = htonl ((uint32_t) q->level);
+  }
+  crc = sc_array_checksum (checkarray);
+
+  if (own_check) {
+    sc_array_destroy (checkarray);
+  }
+
+  return crc;
+}
+
+int
+p4est_tree_is_sorted (p4est_tree_t * tree)
+{
+  size_t              iz;
+  p4est_quadrant_t   *q1, *q2;
+  sc_array_t         *tquadrants = &tree->quadrants;
+
+  if (tquadrants->elem_count <= 1) {
+    return 1;
+  }
+
+  q1 = p4est_quadrant_array_index (tquadrants, 0);
+  for (iz = 1; iz < tquadrants->elem_count; ++iz) {
+    q2 = p4est_quadrant_array_index (tquadrants, iz);
+    if (p4est_quadrant_compare (q1, q2) >= 0) {
+      return 0;
+    }
+    q1 = q2;
+  }
+
+  return 1;
+}
+
+int
+p4est_tree_is_linear (p4est_tree_t * tree)
+{
+  size_t              iz;
+  p4est_quadrant_t   *q1, *q2;
+  sc_array_t         *tquadrants = &tree->quadrants;
+
+  if (tquadrants->elem_count <= 1) {
+    return 1;
+  }
+
+  q1 = p4est_quadrant_array_index (tquadrants, 0);
+  for (iz = 1; iz < tquadrants->elem_count; ++iz) {
+    q2 = p4est_quadrant_array_index (tquadrants, iz);
+    if (p4est_quadrant_compare (q1, q2) >= 0) {
+      return 0;
+    }
+    if (p4est_quadrant_is_ancestor (q1, q2)) {
+      return 0;
+    }
+    q1 = q2;
+  }
+
+  return 1;
+}
+
+int
+p4est_tree_is_almost_sorted (p4est_tree_t * tree, int check_linearity)
+{
+  size_t              iz;
+  int                 face_contact1;
+  int                 face_contact2;
+  int                 out_axis[P4EST_DIM];
+  p4est_quadrant_t   *q1, *q2;
+  sc_array_t         *tquadrants = &tree->quadrants;
+
+  if (tquadrants->elem_count <= 1) {
+    return 1;
+  }
+
+  q1 = p4est_quadrant_array_index (tquadrants, 0);
+  face_contact1 = 0;
+  face_contact1 |= ((q1->x < 0) ? 0x01 : 0);
+  face_contact1 |= ((q1->x >= P4EST_ROOT_LEN) ? 0x02 : 0);
+  face_contact1 |= ((q1->y < 0) ? 0x04 : 0);
+  face_contact1 |= ((q1->y >= P4EST_ROOT_LEN) ? 0x08 : 0);
+#ifdef P4_TO_P8
+  face_contact1 |= ((q1->z < 0) ? 0x10 : 0);
+  face_contact1 |= ((q1->z >= P4EST_ROOT_LEN) ? 0x20 : 0);
+#endif
+  for (iz = 1; iz < tquadrants->elem_count; ++iz) {
+    q2 = p4est_quadrant_array_index (tquadrants, iz);
+    face_contact2 = 0;
+    face_contact2 |= ((q2->x < 0) ? 0x01 : 0);
+    face_contact2 |= ((q2->x >= P4EST_ROOT_LEN) ? 0x02 : 0);
+    face_contact2 |= ((q2->y < 0) ? 0x04 : 0);
+    face_contact2 |= ((q2->y >= P4EST_ROOT_LEN) ? 0x08 : 0);
+#ifdef P4_TO_P8
+    face_contact2 |= ((q2->z < 0) ? 0x10 : 0);
+    face_contact2 |= ((q2->z >= P4EST_ROOT_LEN) ? 0x20 : 0);
+#endif
+    out_axis[0] = face_contact2 & 0x03;
+    out_axis[1] = face_contact2 & 0x0c;
+#ifdef P4_TO_P8
+    out_axis[2] = face_contact2 & 0x30;
+#endif
+    if (((out_axis[0] && out_axis[1])
+#ifdef P4_TO_P8
+         || (out_axis[0] && out_axis[2])
+         || (out_axis[1] && out_axis[2])
+#endif
+        ) && face_contact1 == face_contact2) {
+      /* both quadrants are outside the same edge/corner and can overlap */
+    }
+    else {
+      if (p4est_quadrant_compare (q1, q2) >= 0) {
+        return 0;
+      }
+      if (check_linearity && p4est_quadrant_is_ancestor (q1, q2)) {
+        return 0;
+      }
+    }
+    q1 = q2;
+    face_contact1 = face_contact2;
+  }
+
+  return 1;
+}
+
+int
+p4est_tree_is_complete (p4est_tree_t * tree)
+{
+  size_t              iz;
+  p4est_quadrant_t   *q1, *q2;
+  sc_array_t         *tquadrants = &tree->quadrants;
+
+  if (tquadrants->elem_count <= 1) {
+    return 1;
+  }
+
+  q1 = p4est_quadrant_array_index (tquadrants, 0);
+  for (iz = 1; iz < tquadrants->elem_count; ++iz) {
+    q2 = p4est_quadrant_array_index (tquadrants, iz);
+    if (!p4est_quadrant_is_next (q1, q2)) {
+      return 0;
+    }
+    q1 = q2;
+  }
+
+  return 1;
+}
+
+void
+p4est_tree_print (int log_priority, p4est_tree_t * tree)
+{
+  size_t              jz;
+  int                 l, childid, comp;
+  char                buffer[BUFSIZ];
+  p4est_quadrant_t   *q1, *q2;
+  sc_array_t         *tquadrants = &tree->quadrants;
+
+  q1 = NULL;
+  for (jz = 0; jz < tquadrants->elem_count; ++jz) {
+    q2 = p4est_quadrant_array_index (tquadrants, jz);
+    childid = p4est_quadrant_child_id (q2);
+#ifdef P4_TO_P8
+    l = snprintf (buffer, BUFSIZ, "0x%llx 0x%llx 0x%llx %d",
+                  (unsigned long long) q2->x, (unsigned long long) q2->y,
+                  (unsigned long long) q2->z, (int) q2->level);
+#else
+    l = snprintf (buffer, BUFSIZ, "0x%llx 0x%llx %d",
+                  (unsigned long long) q2->x, (unsigned long long) q2->y,
+                  (int) q2->level);
+#endif
+    if (jz > 0) {
+      comp = p4est_quadrant_compare (q1, q2);
+      if (comp > 0) {
+        l += snprintf (buffer + l, BUFSIZ - l, " R");
+      }
+      else if (comp == 0) {
+        l += snprintf (buffer + l, BUFSIZ - l, " I");
+      }
+      else {
+        if (p4est_quadrant_is_sibling (q1, q2)) {
+          l += snprintf (buffer + l, BUFSIZ - l, " S%d", childid);
+        }
+        else if (p4est_quadrant_is_parent (q1, q2)) {
+          l += snprintf (buffer + l, BUFSIZ - l, " C%d", childid);
+        }
+        else if (p4est_quadrant_is_ancestor (q1, q2)) {
+          l += snprintf (buffer + l, BUFSIZ - l, " D");
+        }
+        else if (p4est_quadrant_is_next (q1, q2)) {
+          l += snprintf (buffer + l, BUFSIZ - l, " N%d", childid);
+        }
+        else {
+          l += snprintf (buffer + l, BUFSIZ - l, " q%d", childid);
+        }
+      }
+    }
+    else {
+      l += snprintf (buffer + l, BUFSIZ - l, " F%d", childid);
+    }
+    l += snprintf (buffer + l, BUFSIZ - l, "\n");
+    P4EST_LOG (log_priority, buffer);
+    q1 = q2;
+  }
+}
+
+int
+p4est_is_equal (p4est_t * p4est1, p4est_t * p4est2, int compare_data)
+{
+  int                 i;
+  size_t              zz;
+  size_t              data_size;
+  p4est_topidx_t      jt;
+  p4est_tree_t       *tree1, *tree2;
+  p4est_quadrant_t   *q1, *q2;
+  sc_array_t         *tqs1, *tqs2;
+
+  if (p4est1->mpisize != p4est2->mpisize)
+    return 0;
+  if (p4est1->mpirank != p4est2->mpirank)
+    return 0;
+  if (compare_data) {
+    if (p4est1->data_size != p4est2->data_size)
+      return 0;
+    data_size = p4est1->data_size;
+    if (data_size == 0) {
+      compare_data = 0;
+    }
+  }
+  else {
+    data_size = 0;
+  }
+
+  if (p4est1->first_local_tree != p4est2->first_local_tree)
+    return 0;
+  if (p4est1->last_local_tree != p4est2->last_local_tree)
+    return 0;
+  if (p4est1->local_num_quadrants != p4est2->local_num_quadrants)
+    return 0;
+  if (p4est1->global_num_quadrants != p4est2->global_num_quadrants)
+    return 0;
+
+  if (memcmp (p4est1->global_first_quadrant, p4est2->global_first_quadrant,
+              (p4est1->mpisize + 1) * sizeof (p4est_gloidx_t)))
+    return 0;
+  if (memcmp (p4est1->global_first_position, p4est2->global_first_position,
+              (p4est1->mpisize + 1) * sizeof (p4est_quadrant_t)))
+    return 0;
+
+  for (jt = p4est1->first_local_tree; jt <= p4est1->last_local_tree; ++jt) {
+    tree1 = p4est_tree_array_index (p4est1->trees, jt);
+    tqs1 = &tree1->quadrants;
+    tree2 = p4est_tree_array_index (p4est2->trees, jt);
+    tqs2 = &tree2->quadrants;
+
+    if (!p4est_quadrant_is_equal (&tree1->first_desc, &tree2->first_desc))
+      return 0;
+    if (!p4est_quadrant_is_equal (&tree1->last_desc, &tree2->last_desc))
+      return 0;
+    if (tree1->quadrants_offset != tree2->quadrants_offset)
+      return 0;
+
+    for (i = 0; i <= P4EST_MAXLEVEL; ++i) {
+      if (tree1->quadrants_per_level[i] != tree2->quadrants_per_level[i])
+        return 0;
+    }
+    if (tree1->maxlevel != tree2->maxlevel)
+      return 0;
+
+    if (tqs1->elem_count != tqs2->elem_count)
+      return 0;
+    for (zz = 0; zz < tqs1->elem_count; ++zz) {
+      q1 = p4est_quadrant_array_index (tqs1, zz);
+      q2 = p4est_quadrant_array_index (tqs2, zz);
+      if (!p4est_quadrant_is_equal (q1, q2))
+        return 0;
+      if (compare_data
+          && memcmp (q1->p.user_data, q2->p.user_data, data_size))
+        return 0;
+    }
+  }
+
+  return 1;
+}
+
+int
+p4est_is_valid (p4est_t * p4est)
+{
+  const int           num_procs = p4est->mpisize;
+  const int           rank = p4est->mpirank;
+  const p4est_topidx_t first_tree = p4est->first_local_tree;
+  const p4est_topidx_t last_tree = p4est->last_local_tree;
+  int                 i, maxlevel;
+  int                 failed;
+  p4est_topidx_t      jt, next_tree;
+  p4est_locidx_t      nquadrants, lquadrants, perlevel;
+  p4est_qcoord_t      mh = P4EST_QUADRANT_LEN (P4EST_QMAXLEVEL);
+  p4est_quadrant_t   *q;
+  p4est_quadrant_t    mylow, nextlow, s;
+  p4est_tree_t       *tree;
+
+  failed = 0;
+  P4EST_QUADRANT_INIT (&mylow);
+  P4EST_QUADRANT_INIT (&nextlow);
+  P4EST_QUADRANT_INIT (&s);
+
+#ifdef P4EST_ENABLE_DEBUG
+  /* check last item of global partition */
+  P4EST_ASSERT (p4est->global_first_position[num_procs].p.which_tree ==
+                p4est->connectivity->num_trees &&
+                p4est->global_first_position[num_procs].x == 0 &&
+                p4est->global_first_position[num_procs].y == 0);
+#ifdef P4_TO_P8
+  P4EST_ASSERT (p4est->global_first_position[num_procs].z == 0);
+#endif
+  P4EST_ASSERT (p4est->connectivity->num_trees ==
+                (p4est_topidx_t) p4est->trees->elem_count);
+  for (i = 0; i <= num_procs; ++i) {
+    P4EST_ASSERT (p4est->global_first_position[i].level == P4EST_QMAXLEVEL);
+  }
+#endif /* P4EST_ENABLE_DEBUG */
+
+  /* check first tree in global partition */
+  if (first_tree < 0) {
+    if (!(first_tree == -1 && last_tree == -2)) {
+      P4EST_NOTICE ("p4est invalid empty tree range A");
+      failed = 1;
+      goto failtest;
+    }
+  }
+  else {
+    if (p4est->global_first_position[rank].p.which_tree != first_tree) {
+      P4EST_NOTICE ("p4est invalid first tree\n");
+      failed = 1;
+      goto failtest;
+    }
+    mylow.x = p4est->global_first_position[rank].x;
+    mylow.y = p4est->global_first_position[rank].y;
+#ifdef P4_TO_P8
+    mylow.z = p4est->global_first_position[rank].z;
+#endif
+    mylow.level = P4EST_QMAXLEVEL;
+    tree = p4est_tree_array_index (p4est->trees, first_tree);
+    if (tree->quadrants.elem_count > 0) {
+      q = p4est_quadrant_array_index (&tree->quadrants, 0);
+      if (q->x != mylow.x || q->y != mylow.y ||
+#ifdef P4_TO_P8
+          q->z != mylow.z ||
+#endif
+          0) {
+        P4EST_NOTICE ("p4est invalid low quadrant\n");
+        failed = 1;
+        goto failtest;
+      }
+    }
+  }
+
+  /* check last tree in global partition */
+  if (last_tree < 0) {
+    if (!(first_tree == -1 && last_tree == -2)) {
+      P4EST_NOTICE ("p4est invalid empty tree range B");
+      failed = 1;
+      goto failtest;
+    }
+  }
+  else {
+    next_tree = p4est->global_first_position[rank + 1].p.which_tree;
+    if (next_tree != last_tree && next_tree != last_tree + 1) {
+      P4EST_NOTICE ("p4est invalid last tree\n");
+      failed = 1;
+      goto failtest;
+    }
+    nextlow.x = p4est->global_first_position[rank + 1].x;
+    nextlow.y = p4est->global_first_position[rank + 1].y;
+#ifdef P4_TO_P8
+    nextlow.z = p4est->global_first_position[rank + 1].z;
+#endif
+    nextlow.level = P4EST_QMAXLEVEL;
+    if (next_tree == last_tree + 1) {
+      if (nextlow.x != 0 || nextlow.y != 0
+#ifdef P4_TO_P8
+          || nextlow.z != 0
+#endif
+        ) {
+        P4EST_NOTICE ("p4est invalid next coordinates\n");
+        failed = 1;
+        goto failtest;
+      }
+    }
+    tree = p4est_tree_array_index (p4est->trees, last_tree);
+    if (tree->quadrants.elem_count > 0) {
+      q =
+        p4est_quadrant_array_index (&tree->quadrants,
+                                    tree->quadrants.elem_count - 1);
+      if (next_tree == last_tree) {
+        if (!p4est_quadrant_is_next (q, &nextlow)) {
+          P4EST_NOTICE ("p4est invalid next quadrant\n");
+          failed = 1;
+          goto failtest;
+        }
+      }
+      else {
+        p4est_quadrant_last_descendant (q, &s, P4EST_QMAXLEVEL);
+        if (s.x + mh != P4EST_ROOT_LEN || s.y + mh != P4EST_ROOT_LEN ||
+#ifdef P4_TO_P8
+            s.z + mh != P4EST_ROOT_LEN ||
+#endif
+            0) {
+          P4EST_NOTICE ("p4est invalid last quadrant\n");
+          failed = 1;
+          goto failtest;
+        }
+      }
+    }
+  }
+
+  /* check individual trees */
+  lquadrants = 0;
+  for (jt = 0; jt < (p4est_topidx_t) p4est->trees->elem_count; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    if (tree->quadrants_offset != lquadrants) {
+      P4EST_NOTICE ("p4est invalid quadrants offset\n");
+      failed = 1;
+      goto failtest;
+    }
+    if (!p4est_tree_is_complete (tree)) {
+      P4EST_NOTICE ("p4est invalid not complete\n");
+      failed = 1;
+      goto failtest;
+    }
+    if (tree->quadrants.elem_count > 0) {
+      if (jt < p4est->first_local_tree || jt > p4est->last_local_tree) {
+        P4EST_NOTICE ("p4est invalid outside count\n");
+        failed = 1;
+        goto failtest;
+      }
+      q = p4est_quadrant_array_index (&tree->quadrants, 0);
+      p4est_quadrant_first_descendant (q, &s, P4EST_QMAXLEVEL);
+      if (!p4est_quadrant_is_equal (&s, &tree->first_desc)) {
+        P4EST_NOTICE ("p4est invalid first tree descendant\n");
+        failed = 1;
+        goto failtest;
+      }
+      q =
+        p4est_quadrant_array_index (&tree->quadrants,
+                                    tree->quadrants.elem_count - 1);
+      p4est_quadrant_last_descendant (q, &s, P4EST_QMAXLEVEL);
+      if (!p4est_quadrant_is_equal (&s, &tree->last_desc)) {
+        P4EST_NOTICE ("p4est invalid last tree descendant\n");
+        failed = 1;
+        goto failtest;
+      }
+    }
+    else {
+      P4EST_QUADRANT_INIT (&s);
+      if (s.level != tree->first_desc.level ||
+          s.level != tree->last_desc.level) {
+        P4EST_NOTICE ("p4est invalid empty descendant\n");
+        failed = 1;
+        goto failtest;
+      }
+    }
+
+    maxlevel = 0;
+    nquadrants = 0;
+    for (i = 0; i <= P4EST_QMAXLEVEL; ++i) {
+      perlevel = tree->quadrants_per_level[i];
+
+      P4EST_ASSERT (perlevel >= 0);
+      nquadrants += perlevel;   /* same type */
+      if (perlevel > 0) {
+        maxlevel = i;
+      }
+    }
+    for (; i <= P4EST_MAXLEVEL; ++i) {
+      P4EST_ASSERT (tree->quadrants_per_level[i] == -1);
+    }
+    lquadrants += nquadrants;   /* same type */
+
+    if (maxlevel != (int) tree->maxlevel) {
+      P4EST_NOTICE ("p4est invalid wrong maxlevel\n");
+      failed = 1;
+      goto failtest;
+    }
+    if (nquadrants != (p4est_locidx_t) tree->quadrants.elem_count) {
+      P4EST_NOTICE ("p4est invalid tree quadrant count\n");
+      failed = 1;
+      goto failtest;
+    }
+  }
+
+  if (lquadrants != p4est->local_num_quadrants) {
+    P4EST_NOTICE ("p4est invalid local quadrant count\n");
+    failed = 1;
+    goto failtest;
+  }
+
+  if (p4est->global_first_quadrant[0] != 0 ||
+      p4est->global_first_quadrant[num_procs] !=
+      p4est->global_num_quadrants) {
+    P4EST_NOTICE ("p4est invalid global quadrant index\n");
+    failed = 1;
+    goto failtest;
+  }
+
+failtest:
+  return !p4est_comm_sync_flag (p4est, failed, sc_MPI_BOR);
+}
+
+/* here come the heavyweight algorithms */
+#ifndef P4_TO_P8
+/* which face of the center quad touches this insul */
+const static int    insul_to_f[9] = { -1, 2, -1, 0, -1, 1, -1, 3, -1 };
+
+/* which corner of the center quad touches this insul */
+const static int    insul_to_c[9] = { 0, -1, 1, -1, -1, -1, 2, -1, 3 };
+#else
+/* which face of the center quad touches this insul */
+/* *INDENT-OFF* */
+const static int insul_to_f[27] =
+{-1, -1, -1, -1, 4, -1, -1, -1, -1,
+ -1, 2, -1, 0, -1, 1, -1, 3, -1,
+ -1, -1, -1, -1, 5, -1, -1, -1, -1};
+/* which corner of the center quad touches this insul */
+const static int insul_to_c[27] =
+{0, -1, 1, -1, -1, -1, 2, -1, 3,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, -1, 5, -1, -1, -1, 6, -1, 7};
+/* which edge of the center quad touches this insul */
+const static int insul_to_e[27] =
+{-1, 0, -1, 4, -1, 5, -1, 1, -1,
+  8, -1, 9, -1, -1, -1, 10, -1, 11,
+  -1, 2, -1, 6, -1, 7, -1, 3, -1};
+/* *INDENT-ON* */
+#endif
+
+void
+p4est_tree_compute_overlap (p4est_t * p4est, sc_array_t * in,
+                            sc_array_t * out, p4est_connect_type_t balance,
+                            sc_array_t * borders, sc_array_t * inseeds)
+{
+  int                 k, l, m, which;
+  int                 face, corner, level;
+  int                 f = -1, c = -1;
+  int                 ftransform[P4EST_FTRANSFORM];
+  int                 face_axis[3];     /* 3 not P4EST_DIM */
+  int                 contact_face_only, contact_edge_only;
+  int                 inter_tree, outface[P4EST_FACES];
+  size_t              iz, jz, kz, ctree;
+  size_t              treecount, incount, seedcount;
+  size_t              guess, split;
+  ssize_t             first_index, last_index, js;
+  p4est_topidx_t      qtree, ntree, first_tree, ftree = -1;
+  p4est_qcoord_t      qh;
+  p4est_quadrant_t    fd, ld, tempq, ins[P4EST_INSUL];
+  p4est_quadrant_t   *treefd, *treeld;
+  p4est_quadrant_t   *tq, *s, *u;
+  p4est_quadrant_t   *inq, *outq;
+  p4est_tree_t       *tree;
+  p4est_connectivity_t *conn = p4est->connectivity;
+#ifdef P4_TO_P8
+  int                 edge;
+  int                 e = -1;
+  size_t              etree;
+  p8est_edge_info_t   ei;
+  p8est_edge_transform_t *et;
+  sc_array_t         *eta;
+  p4est_quadrant_t    tempq1, tempq2;
+#endif
+  p4est_corner_info_t ci;
+  p4est_corner_transform_t *ct;
+  sc_array_t         *cta;
+  sc_array_t         *tquadrants;
+  sc_array_t         *seeds = NULL;
+  p4est_quadrant_t   *neigharray[P4EST_CHILDREN];
+  size_t              nneigh = -1;
+
+  P4EST_QUADRANT_INIT (&fd);
+  P4EST_QUADRANT_INIT (&ld);
+  P4EST_QUADRANT_INIT (&tempq);
+#ifdef P4_TO_P8
+  P4EST_QUADRANT_INIT (&tempq1);
+  P4EST_QUADRANT_INIT (&tempq2);
+#endif
+  for (which = 0; which < P4EST_INSUL; ++which) {
+    P4EST_QUADRANT_INIT (&ins[which]);
+  }
+#ifdef P4_TO_P8
+  eta = &ei.edge_transforms;
+  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+#endif
+  cta = &ci.corner_transforms;
+  sc_array_init (cta, sizeof (p4est_corner_transform_t));
+
+  /* assign incoming quadrant count */
+  incount = in->elem_count;
+
+  /* initialize the tracking of trees */
+  qtree = -1;
+  tree = NULL;
+  treefd = treeld = NULL;
+  tquadrants = NULL;
+  treecount = -1;
+
+  seeds = sc_array_new (sizeof (p4est_quadrant_t));
+  first_tree = p4est->first_local_tree;
+
+  /* loop over input list of quadrants */
+  for (iz = 0; iz < incount; ++iz) {
+    inq = p4est_quadrant_array_index (in, iz);
+
+    P4EST_ASSERT (inq->p.piggy2.from_tree >= 0 &&
+                  inq->p.piggy2.from_tree < p4est->connectivity->num_trees);
+    ftree = inq->p.piggy2.from_tree;
+    nneigh = 0;
+
+    /* potentially grab new tree */
+    if (inq->p.piggy2.which_tree != qtree) {
+      P4EST_ASSERT (qtree < inq->p.piggy2.which_tree);
+      qtree = inq->p.piggy2.which_tree;
+
+      tree = p4est_tree_array_index (p4est->trees, qtree);
+      treefd = &tree->first_desc;
+      treeld = &tree->last_desc;
+      if (borders == NULL) {
+        tquadrants = &tree->quadrants;
+      }
+      else {
+        tquadrants = (sc_array_t *) sc_array_index_int (borders, (int)
+                                                        (qtree - first_tree));
+      }
+      treecount = tquadrants->elem_count;
+      P4EST_ASSERT (treecount > 0);
+    }
+
+    inter_tree = 0;
+    ntree = -1;
+    face = corner = -1;
+#ifdef P4_TO_P8
+    edge = -1;
+    ei.iedge = -1;
+    et = NULL;
+#endif
+    ci.icorner = -1;
+    ct = NULL;
+    contact_face_only = contact_edge_only = 0;
+    if (!p4est_quadrant_is_inside_root (inq)) {
+      /* this quadrant comes from a different tree */
+      P4EST_ASSERT (p4est_quadrant_is_extended (inq));
+      inter_tree = 1;
+      outface[0] = (inq->x < 0);
+      outface[1] = (inq->x >= P4EST_ROOT_LEN);
+      face_axis[0] = outface[0] || outface[1];
+      outface[2] = (inq->y < 0);
+      outface[3] = (inq->y >= P4EST_ROOT_LEN);
+      face_axis[1] = outface[2] || outface[3];
+#ifndef P4_TO_P8
+      face_axis[2] = 0;
+#else
+      outface[4] = (inq->z < 0);
+      outface[5] = (inq->z >= P4EST_ROOT_LEN);
+      face_axis[2] = outface[4] || outface[5];
+#endif
+      if (!face_axis[1] && !face_axis[2]) {
+        contact_face_only = 1;
+        face = 0 + outface[1];
+      }
+      else if (!face_axis[0] && !face_axis[2]) {
+        contact_face_only = 1;
+        face = 2 + outface[3];
+      }
+#ifdef P4_TO_P8
+      else if (!face_axis[0] && !face_axis[1]) {
+        contact_face_only = 1;
+        face = 4 + outface[5];
+      }
+      else if (!face_axis[0]) {
+        contact_edge_only = 1;
+        edge = 0 + 2 * outface[5] + outface[3];
+      }
+      else if (!face_axis[1]) {
+        contact_edge_only = 1;
+        edge = 4 + 2 * outface[5] + outface[1];
+      }
+      else if (!face_axis[2]) {
+        contact_edge_only = 1;
+        edge = 8 + 2 * outface[3] + outface[1];
+      }
+#endif
+      if (contact_face_only) {
+        P4EST_ASSERT (!contact_edge_only && face >= 0 && face < P4EST_FACES);
+        P4EST_ASSERT (outface[face]);
+        ntree = p4est_find_face_transform (conn, qtree, face, ftransform);
+        P4EST_ASSERT (ntree >= 0);
+      }
+#ifdef P4_TO_P8
+      else if (contact_edge_only) {
+        P4EST_ASSERT (!contact_face_only && edge >= 0 && edge < P8EST_EDGES);
+        p8est_find_edge_transform (conn, qtree, edge, &ei);
+        P4EST_ASSERT (ei.edge_transforms.elem_count > 0);
+      }
+#endif
+      else {
+        /* outside across a corner */
+        P4EST_ASSERT (face_axis[0] && face_axis[1]);
+        corner = outface[1] + 2 * outface[3];
+#ifdef P4_TO_P8
+        P4EST_ASSERT (face_axis[2]);
+        corner += 4 * outface[5];
+#endif
+        P4EST_ASSERT (p4est_quadrant_touches_corner (inq, corner, 0));
+        p4est_find_corner_transform (conn, qtree, corner, &ci);
+        P4EST_ASSERT (ci.corner_transforms.elem_count > 0);
+      }
+    }
+    qh = P4EST_QUADRANT_LEN (inq->level);
+
+    /* loop over the insulation layer of inq */
+#ifdef P4_TO_P8
+    for (m = 0; m < 3; ++m) {
+#if 0
+    }
+#endif
+#else
+    m = 0;
+#endif
+    for (k = 0; k < 3; ++k) {
+      for (l = 0; l < 3; ++l) {
+        which = m * 9 + k * 3 + l;      /* 2D: 0..8, 3D: 0..26 */
+
+        /* exclude myself from the queries */
+        if (which == P4EST_INSUL / 2) {
+          continue;
+        }
+        s = &ins[which];
+        *s = *inq;
+        s->x += (l - 1) * qh;
+        s->y += (k - 1) * qh;
+#ifdef P4_TO_P8
+        s->z += (m - 1) * qh;
+#endif
+        if ((s->x < 0 || s->x >= P4EST_ROOT_LEN) ||
+            (s->y < 0 || s->y >= P4EST_ROOT_LEN) ||
+#ifdef P4_TO_P8
+            (s->z < 0 || s->z >= P4EST_ROOT_LEN) ||
+#endif
+            0) {
+          /* this quadrant is outside this tree, no overlap */
+          continue;
+        }
+        p4est_quadrant_first_descendant (s, &fd, P4EST_QMAXLEVEL);
+        p4est_quadrant_last_descendant (s, &ld, P4EST_QMAXLEVEL);
+
+        /* skip this insulation quadrant if there is no overlap */
+        if (p4est_quadrant_compare (&ld, treefd) < 0 ||
+            p4est_quadrant_compare (treeld, &fd) < 0) {
+          continue;
+        }
+
+        /* Find last quadrant in tree <= ld */
+        guess = treecount / 2;
+        if (p4est_quadrant_compare (treeld, &ld) <= 0) {
+          /* the last tree quadrant overlaps an insulation quadrant */
+          last_index = (ssize_t) treecount - 1;
+        }
+        else {
+          /* do a binary search for the highest tree quadrant <= ld */
+          last_index = p4est_find_higher_bound (tquadrants, &ld, guess);
+          if (last_index < 0) {
+            SC_ABORT_NOT_REACHED ();
+          }
+          guess = (size_t) last_index;
+        }
+
+        if (p4est_quadrant_compare (&fd, treefd) < 0) {
+          /* the first tree quadrant overlaps an insulation quadrant */
+          first_index = 0;
+        }
+        else {
+          /* Do a binary search for the lowest tree quadrant >= s.
+             Does not accept a strict ancestor of s, which is on purpose. */
+          first_index = p4est_find_lower_bound (tquadrants, s, guess);
+        }
+
+        if (first_index < 0 || first_index > last_index ||
+            p4est_quadrant_compare (&fd, &ld) == 0) {
+          /* The only possibility is that a quadrant tq larger than s
+           * contains s, or that tq and s are of smallest possible size */
+          tq = p4est_quadrant_array_index (tquadrants, last_index);
+          P4EST_ASSERT (p4est_quadrant_is_ancestor (tq, s) ||
+                        (p4est_quadrant_is_equal (tq, s) &&
+                         tq->level == P4EST_QMAXLEVEL));
+          if (tq->level < s->level - 1) {
+            for (kz = 0; kz < nneigh; kz++) {
+              if (neigharray[kz] == tq) {
+                break;
+              }
+            }
+            /* if this neighbor hasn't been calculated */
+            if (kz == nneigh) {
+              /* we should check to see if inq causes a split to tq */
+              split = p4est_balance_seeds (inq, tq, balance, seeds);
+
+              if (split) {
+                seedcount = seeds->elem_count;
+                for (jz = 0; jz < seedcount; jz++) {
+                  u = p4est_quadrant_array_index (seeds, jz);
+                  P4EST_ASSERT (p4est_quadrant_is_ancestor (tq, u));
+
+                  outq = (p4est_quadrant_t *) sc_array_push (inseeds);
+                  p4est_quadrant_sibling (u, outq, 0);
+                  outq->p.piggy2.which_tree = qtree;
+                }
+              }
+              P4EST_ASSERT (nneigh < P4EST_CHILDREN - 1);
+              neigharray[nneigh++] = tq;
+            }
+          }
+          continue;
+        }
+
+        /* figure out the relationship of s to inq */
+        f = insul_to_f[which];
+#ifdef P4_TO_P8
+        e = insul_to_e[which];
+#endif
+        c = insul_to_c[which];
+
+        level = inq->level + 1;
+
+        /* copy relevant quadrants into out */
+        for (js = first_index; js <= last_index; ++js) {
+          tq = p4est_quadrant_array_index (tquadrants, (size_t) js);
+          if (tq->level <= level) {
+            continue;
+          }
+          if (f >= 0) {
+            p4est_quadrant_face_neighbor (tq, f ^ 1, &tempq);
+            if (p4est_quadrant_is_ancestor (inq, &tempq)) {
+              continue;
+            }
+            split = p4est_balance_seeds_face (tq, inq, f, balance, seeds);
+          }
+#ifdef P4_TO_P8
+          else if (e >= 0) {
+            p8est_quadrant_edge_neighbor (tq, e ^ 3, &tempq);
+            if (p4est_quadrant_is_ancestor (inq, &tempq)) {
+              continue;
+            }
+            split = p8est_balance_seeds_edge (tq, inq, e, balance, seeds);
+          }
+#endif
+          else {
+            P4EST_ASSERT (c >= 0);
+            p4est_quadrant_corner_neighbor (tq, (P4EST_CHILDREN - 1) ^ c,
+                                            &tempq);
+            if (p4est_quadrant_is_ancestor (inq, &tempq)) {
+              continue;
+            }
+            split = p4est_balance_seeds_corner (tq, inq, c, balance, seeds);
+          }
+          if (split) {
+            seedcount = seeds->elem_count;
+            for (jz = 0; jz < seedcount; jz++) {
+              u = p4est_quadrant_array_index (seeds, jz);
+              P4EST_ASSERT (p4est_quadrant_is_ancestor (inq, u));
+              if (inter_tree) {
+                if (contact_face_only) {
+                  P4EST_ASSERT (!contact_edge_only);
+                  P4EST_ASSERT (ntree == ftree);
+                  p4est_quadrant_transform_face (u, &tempq, ftransform);
+                  outq = p4est_quadrant_array_push (out);
+                  p4est_quadrant_sibling (&tempq, outq, 0);
+                  outq->p.piggy2.which_tree = ntree;
+                }
+#ifdef P4_TO_P8
+                else if (contact_edge_only) {
+                  P4EST_ASSERT (inq->pad16 >= 0 && inq->pad16 < P8EST_EDGES);
+                  for (etree = 0; etree < eta->elem_count; ++etree) {
+                    et = p8est_edge_array_index (eta, etree);
+                    if (et->ntree == ftree && et->nedge == inq->pad16) {
+                      p8est_quadrant_transform_edge (u, &tempq, &ei, et, 1);
+                      outq = p4est_quadrant_array_push (out);
+                      p4est_quadrant_sibling (&tempq, outq, 0);
+                      outq->p.piggy2.which_tree = et->ntree;
+                    }
+                  }
+                  et = NULL;
+                }
+#endif
+                else {
+                  P4EST_ASSERT (corner >= 0);
+                  P4EST_ASSERT (inq->pad16 >= 0 &&
+                                inq->pad16 < P4EST_CHILDREN);
+                  for (ctree = 0; ctree < cta->elem_count; ++ctree) {
+                    ct = p4est_corner_array_index (cta, ctree);
+                    if (ct->ntree == ftree && ct->ncorner == inq->pad16) {
+                      p4est_quadrant_transform_corner (u, (int) ct->ncorner,
+                                                       1);
+                      outq = p4est_quadrant_array_push (out);
+                      p4est_quadrant_sibling (u, outq, 0);
+                      outq->p.piggy2.which_tree = ct->ntree;
+                    }
+                  }
+                  ct = NULL;
+                }
+              }
+              else {
+                outq = p4est_quadrant_array_push (out);
+                p4est_quadrant_sibling (u, outq, 0);
+                outq->p.piggy2.which_tree = qtree;
+              }
+
+              if (c >= 0) {
+                level = SC_MAX (level, u->level);
+              }
+            }
+          }
+        }
+      }
+    }
+#ifdef P4_TO_P8
+#if 0
+    {
+#endif
+    }
+#endif
+  }
+
+#ifdef P4_TO_P8
+  sc_array_reset (eta);
+#endif
+  sc_array_reset (cta);
+
+  sc_array_destroy (seeds);
+}
+
+void
+p4est_tree_uniqify_overlap (sc_array_t * out)
+{
+  size_t              iz, jz;
+  size_t              outcount, dupcount, olcount;
+  p4est_quadrant_t   *q, *p, tempq;
+
+  outcount = out->elem_count;
+  if (outcount == 0) {
+    return;
+  }
+
+  /* sort array and remove duplicates */
+  sc_array_sort (out, p4est_quadrant_compare_piggy);
+  dupcount = olcount = 0;
+  iz = 0;                       /* read counter */
+  jz = 0;                       /* write counter */
+  q = NULL;
+  for (iz = 0; iz < outcount; iz++) {
+    p = p4est_quadrant_array_index (out, iz);
+    P4EST_ASSERT (p4est_quadrant_child_id (p) == 0);
+    if (q != NULL && q->p.piggy2.which_tree == p->p.piggy2.which_tree) {
+      p4est_nearest_common_ancestor (p, q, &tempq);
+      if (tempq.level >= SC_MIN (q->level, p->level) - 1) {
+        if (p->level > q->level) {
+          olcount++;
+          *q = *p;
+        }
+        else {
+          P4EST_ASSERT (p->level == q->level);
+          dupcount++;
+        }
+        continue;
+      }
+    }
+    if (iz == jz) {
+      q = p;
+    }
+    else {
+      q = p4est_quadrant_array_index (out, jz);
+      *q = *p;
+    }
+    jz++;
+  }
+  P4EST_ASSERT (jz + olcount + dupcount == outcount);
+  sc_array_resize (out, jz);
+}
+
+size_t
+p4est_tree_remove_nonowned (p4est_t * p4est, p4est_topidx_t which_tree)
+{
+  int                 full_tree[2];
+  size_t              zz, incount, prev_good, removed;
+#ifdef P4EST_ENABLE_DEBUG
+  const p4est_topidx_t first_tree = p4est->first_local_tree;
+  const p4est_topidx_t last_tree = p4est->last_local_tree;
+#endif
+  const p4est_quadrant_t *first_pos, *next_pos;
+  p4est_quadrant_t   *q1, *q2;
+  p4est_quadrant_t    ld;
+  p4est_tree_t       *tree;
+  sc_array_t         *quadrants;
+
+  P4EST_ASSERT (first_tree <= which_tree && which_tree <= last_tree);
+  tree = p4est_tree_array_index (p4est->trees, which_tree);
+  P4EST_ASSERT (p4est_tree_is_almost_sorted (tree, 0));
+
+  quadrants = &tree->quadrants;
+  incount = quadrants->elem_count;
+  if (incount == 0) {
+    return 0;
+  }
+
+  P4EST_QUADRANT_INIT (&ld);
+  p4est_comm_tree_info (p4est, which_tree, full_tree, NULL,
+                        &first_pos, &next_pos);
+
+  /* q1 is the last known good quadrant */
+  q1 = NULL;
+  prev_good = incount;
+  removed = 0;
+  for (zz = 0; zz < incount; ++zz) {
+    q2 = p4est_quadrant_array_index (quadrants, zz);
+    P4EST_ASSERT (p4est_quadrant_is_extended (q2));
+    if (!p4est_quadrant_is_inside_root (q2) ||
+        (!full_tree[0] &&
+         (p4est_quadrant_compare (q2, first_pos) < 0 &&
+          (q2->x != first_pos->x || q2->y != first_pos->y
+#ifdef P4_TO_P8
+           || q2->z != first_pos->z
+#endif
+          ))) ||
+        (!full_tree[1] &&
+         (p4est_quadrant_last_descendant (q2, &ld, P4EST_QMAXLEVEL),
+          p4est_quadrant_compare (next_pos, &ld) <= 0))) {
+      /* quadrant is outside of the unit square
+         or at least partially outside of the tree bounds */
+      --tree->quadrants_per_level[q2->level];
+      p4est_quadrant_free_data (p4est, q2);
+      ++removed;
+#ifdef P4EST_ENABLE_DEBUG
+      P4EST_QUADRANT_INIT (q2);
+#endif
+    }
+    else {
+      if (prev_good == incount) {
+        /* this is the first good quadrant we find */
+        prev_good = 0;
+      }
+      else {
+        /* q1 at prev_good was the last known good */
+        ++prev_good;
+      }
+      P4EST_ASSERT (prev_good <= zz);
+      q1 = p4est_quadrant_array_index (quadrants, prev_good);
+      if (zz > prev_good) {
+        *q1 = *q2;
+#ifdef P4EST_ENABLE_DEBUG
+        P4EST_QUADRANT_INIT (q2);
+#endif
+      }
+    }
+  }
+
+  if (prev_good == incount) {
+    P4EST_ASSERT (removed == incount);
+    incount = 0;
+  }
+  else {
+    P4EST_ASSERT (prev_good + 1 + removed == incount);
+    incount = prev_good + 1;
+    q1 = p4est_quadrant_array_index (quadrants, 0);
+  }
+  sc_array_resize (quadrants, incount);
+
+  tree->maxlevel = 0;
+  for (zz = 0; zz < incount; ++zz) {
+    q1 = p4est_quadrant_array_index (quadrants, zz);
+    P4EST_ASSERT (p4est_quadrant_is_valid (q1));
+    tree->maxlevel = (int8_t) SC_MAX (tree->maxlevel, q1->level);
+  }
+
+  P4EST_ASSERT (p4est_tree_is_sorted (tree));
+
+  return removed;
+}
+
+void
+p4est_complete_region (p4est_t * p4est,
+                       const p4est_quadrant_t * q1,
+                       int include_q1,
+                       const p4est_quadrant_t * q2,
+                       int include_q2,
+                       p4est_tree_t * tree,
+                       p4est_topidx_t which_tree, p4est_init_t init_fn)
+{
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              quadrant_pool_size, data_pool_size;
+#endif
+
+  p4est_tree_t       *R;
+  sc_list_t          *W;
+
+  p4est_quadrant_t    a = *q1;
+  p4est_quadrant_t    b = *q2;
+
+  p4est_quadrant_t    Afinest;
+  p4est_quadrant_t   *c0, *c1, *c2, *c3;
+#ifdef P4_TO_P8
+  p4est_quadrant_t   *c4, *c5, *c6, *c7;
+#endif
+
+  sc_array_t         *quadrants;
+  sc_mempool_t       *quadrant_pool = p4est->quadrant_pool;
+
+  p4est_quadrant_t   *w;
+  p4est_quadrant_t   *r;
+
+  int                 comp;
+  int                 maxlevel = 0;
+  p4est_locidx_t     *quadrants_per_level;
+
+  P4EST_QUADRANT_INIT (&Afinest);
+
+  W = sc_list_new (NULL);
+  R = tree;
+
+  /* needed for sanity check */
+#ifdef P4EST_ENABLE_DEBUG
+  quadrant_pool_size = p4est->quadrant_pool->elem_count;
+  data_pool_size = 0;
+  if (p4est->user_data_pool != NULL) {
+    data_pool_size = p4est->user_data_pool->elem_count;
+  }
+#endif
+
+  quadrants = &R->quadrants;
+  quadrants_per_level = R->quadrants_per_level;
+
+  /* Assert that we have an empty tree */
+  P4EST_ASSERT (quadrants->elem_count == 0);
+
+  comp = p4est_quadrant_compare (&a, &b);
+  /* Assert that a<b */
+  P4EST_ASSERT (comp < 0);
+
+  /* R <- R + a */
+  if (include_q1) {
+    r = p4est_quadrant_array_push (quadrants);
+    *r = a;
+    p4est_quadrant_init_data (p4est, which_tree, r, init_fn);
+    maxlevel = SC_MAX ((int) r->level, maxlevel);
+    ++quadrants_per_level[r->level];
+  }
+
+  if (comp < 0) {
+    /* W <- C(A_{finest}(a,b)) */
+    p4est_nearest_common_ancestor (&a, &b, &Afinest);
+
+    c0 = p4est_quadrant_mempool_alloc (quadrant_pool);
+    c1 = p4est_quadrant_mempool_alloc (quadrant_pool);
+    c2 = p4est_quadrant_mempool_alloc (quadrant_pool);
+    c3 = p4est_quadrant_mempool_alloc (quadrant_pool);
+#ifdef P4_TO_P8
+    c4 = p4est_quadrant_mempool_alloc (quadrant_pool);
+    c5 = p4est_quadrant_mempool_alloc (quadrant_pool);
+    c6 = p4est_quadrant_mempool_alloc (quadrant_pool);
+    c7 = p4est_quadrant_mempool_alloc (quadrant_pool);
+
+    p8est_quadrant_children (&Afinest, c0, c1, c2, c3, c4, c5, c6, c7);
+#else
+    p4est_quadrant_children (&Afinest, c0, c1, c2, c3);
+#endif
+
+    sc_list_append (W, c0);
+    sc_list_append (W, c1);
+    sc_list_append (W, c2);
+    sc_list_append (W, c3);
+#ifdef P4_TO_P8
+    sc_list_append (W, c4);
+    sc_list_append (W, c5);
+    sc_list_append (W, c6);
+    sc_list_append (W, c7);
+#endif
+
+    /* for each w in W */
+    while (W->elem_count > 0) {
+      w = p4est_quadrant_list_pop (W);
+
+      /* if (a < w < b) and (w not in {A(b)}) */
+      if (((p4est_quadrant_compare (&a, w) < 0) &&
+           (p4est_quadrant_compare (w, &b) < 0)
+          ) && !p4est_quadrant_is_ancestor (w, &b)
+        ) {
+        /* R <- R + w */
+        r = p4est_quadrant_array_push (quadrants);
+        *r = *w;
+        p4est_quadrant_init_data (p4est, which_tree, r, init_fn);
+        maxlevel = SC_MAX ((int) r->level, maxlevel);
+        ++quadrants_per_level[r->level];
+      }
+      /* else if (w in {{A(a)}, {A(b)}}) */
+      else if (p4est_quadrant_is_ancestor (w, &a)
+               || p4est_quadrant_is_ancestor (w, &b)) {
+        /* W <- W + C(w) */
+        c0 = p4est_quadrant_mempool_alloc (quadrant_pool);
+        c1 = p4est_quadrant_mempool_alloc (quadrant_pool);
+        c2 = p4est_quadrant_mempool_alloc (quadrant_pool);
+        c3 = p4est_quadrant_mempool_alloc (quadrant_pool);
+#ifdef P4_TO_P8
+        c4 = p4est_quadrant_mempool_alloc (quadrant_pool);
+        c5 = p4est_quadrant_mempool_alloc (quadrant_pool);
+        c6 = p4est_quadrant_mempool_alloc (quadrant_pool);
+        c7 = p4est_quadrant_mempool_alloc (quadrant_pool);
+
+        p8est_quadrant_children (w, c0, c1, c2, c3, c4, c5, c6, c7);
+#else
+        p4est_quadrant_children (w, c0, c1, c2, c3);
+#endif
+
+#ifdef P4_TO_P8
+        sc_list_prepend (W, c7);
+        sc_list_prepend (W, c6);
+        sc_list_prepend (W, c5);
+        sc_list_prepend (W, c4);
+#endif
+        sc_list_prepend (W, c3);
+        sc_list_prepend (W, c2);
+        sc_list_prepend (W, c1);
+        sc_list_prepend (W, c0);
+      }
+
+      /* W <- W - w */
+      sc_mempool_free (quadrant_pool, w);
+    }                           /* end for */
+
+    /* R <- R + b */
+    if (include_q2) {
+      r = p4est_quadrant_array_push (quadrants);
+      *r = b;
+      p4est_quadrant_init_data (p4est, which_tree, r, init_fn);
+      maxlevel = SC_MAX ((int) r->level, maxlevel);
+      ++quadrants_per_level[r->level];
+    }
+  }
+
+  R->maxlevel = (int8_t) maxlevel;
+
+  P4EST_ASSERT (W->first == NULL && W->last == NULL);
+  sc_list_destroy (W);
+
+  P4EST_ASSERT (p4est_tree_is_complete (R));
+  P4EST_ASSERT (quadrant_pool_size == p4est->quadrant_pool->elem_count);
+  if (p4est->user_data_pool != NULL) {
+    P4EST_ASSERT (data_pool_size + quadrants->elem_count ==
+                  p4est->user_data_pool->elem_count);
+  }
+}
+
+static int
+p4est_quadrant_disjoint_parent (const void *a, const void *b)
+{
+  const p4est_quadrant_t *q = (p4est_quadrant_t *) a;
+  const p4est_quadrant_t *r = (p4est_quadrant_t *) b;
+  int8_t              level = SC_MIN (q->level - 1, r->level - 1);
+  p4est_qcoord_t      mask =
+    ((p4est_qcoord_t) - 1) << (P4EST_MAXLEVEL - level);
+
+  if (((q->x ^ r->x) & mask) || ((q->y ^ r->y) & mask)
+#ifdef P4_TO_P8
+      || ((q->z ^ r->z) & mask)
+#endif
+      || 0) {
+    return p4est_quadrant_compare (a, b);
+  }
+
+  return 0;
+}
+
+/* kernel for balancing quadrants.
+ * inlist: sorted linear array: every quadrant should be child_id == 0
+ * dom: quadrant that is ancestor to all quadrants in \a in.
+ * bound: balance type bound
+ * qpool: quadrant mempool
+ * list_alloc: sc_link_t mempool
+ * out: output complete balance array
+ * first_desc: optional first descendant
+ * last_desct: optional last_descendant
+ * count_in: count_already_inlist accumulator
+ * count_out: count_already_outlist accumulator
+ * count_an: count_ancestor_inlist_accumulator
+ */
+static void
+p4est_complete_or_balance_kernel (sc_array_t * inlist,
+                                  p4est_quadrant_t * dom,
+                                  int bound,
+                                  sc_mempool_t * qpool,
+                                  sc_mempool_t * list_alloc,
+                                  sc_array_t * out,
+                                  p4est_quadrant_t * first_desc,
+                                  p4est_quadrant_t * last_desc,
+                                  size_t * count_in, size_t * count_out,
+                                  size_t * count_an)
+{
+  int                 inserted;
+  size_t              iz, jz;
+  size_t              incount, ocount;
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              quadrant_pool_size;
+  sc_array_t          outview;
+#endif
+  size_t              count_already_inlist, count_already_outlist;
+  size_t              count_ancestor_inlist;
+  p4est_quadrant_t   *q, *p, *r;
+  int                 minlevel = dom->level + 1, maxlevel;
+  int                 sid, pid;
+  int                 duplicate = 1;
+  int                 precluded = 2;
+  int                 l;
+  void              **vlookup;
+  ssize_t             srindex, si;
+  p4est_qcoord_t      ph;
+  p4est_quadrant_t   *qalloc, *qlookup, **qpointer;
+  p4est_quadrant_t    par, tempq, tempp, fd, ld;
+  uint64_t            lid;
+  sc_array_t         *olist;
+  sc_hash_t          *hash[P4EST_MAXLEVEL + 1];
+  sc_array_t          outlist[P4EST_MAXLEVEL + 1];
+
+  P4EST_QUADRANT_INIT (&par);
+  par.p.user_int = 0;
+  P4EST_QUADRANT_INIT (&tempq);
+  P4EST_QUADRANT_INIT (&tempp);
+  P4EST_QUADRANT_INIT (&fd);
+
+#ifdef P4EST_ENABLE_DEBUG
+  quadrant_pool_size = qpool->elem_count;
+#endif
+
+  count_already_inlist = count_already_outlist = 0;
+  count_ancestor_inlist = 0;
+
+  incount = inlist->elem_count;
+  maxlevel = minlevel;
+  for (jz = 0; jz < incount; jz++) {
+    q = p4est_quadrant_array_index (inlist, jz);
+    q->p.user_int = 0;
+    maxlevel = SC_MAX (maxlevel, q->level);
+    P4EST_ASSERT (p4est_quadrant_is_ancestor (dom, q));
+    P4EST_ASSERT (p4est_quadrant_child_id (q) == 0);
+  }
+
+  if (first_desc != NULL) {
+    /* make sure that a quadrant at first_desc is represented in inlist */
+    fd = *first_desc;
+    while (fd.level > minlevel && p4est_quadrant_child_id (&fd) == 0) {
+      p4est_quadrant_parent (&fd, &fd);
+    }
+    p4est_quadrant_sibling (&fd, &tempq, 0);
+    si = p4est_find_lower_bound (inlist, &tempq, 0);
+    if (si >= 0) {
+      q = p4est_quadrant_array_index (inlist, si);
+      p4est_nearest_common_ancestor (&tempq, q, &tempp);
+      if (tempp.level < tempq.level - 1) {
+        /* add tempq to inlist */
+        sc_array_resize (inlist, inlist->elem_count + 1);
+        memmove (sc_array_index (inlist, si + 1), sc_array_index (inlist, si),
+                 incount - si);
+        q = p4est_quadrant_array_index (inlist, si);
+        *q = tempq;
+        q->p.user_int = 0;
+        incount++;
+      }
+    }
+    else {
+      /* add tempq to inlist */
+      q = (p4est_quadrant_t *) sc_array_push (inlist);
+      *q = tempq;
+      q->p.user_int = 0;
+      incount++;
+    }
+  }
+  else {
+    p4est_quadrant_first_descendant (dom, &fd, minlevel);
+  }
+
+  if (last_desc != NULL) {
+    /* make sure that a quadrant at last_desc is represented in inlist */
+    tempq = *last_desc;
+    while (tempq.level > minlevel &&
+           p4est_quadrant_child_id (&tempq) == P4EST_CHILDREN - 1) {
+      p4est_quadrant_parent (&tempq, &tempq);
+    }
+    p4est_quadrant_sibling (&tempq, &tempp, 0);
+    si = p4est_find_higher_bound (inlist, last_desc, 0);
+    if (si >= 0) {
+      q = p4est_quadrant_array_index (inlist, si);
+      p4est_nearest_common_ancestor (last_desc, q, &tempq);
+      if (tempq.level < tempp.level - 1) {
+        /* add tempp to inlist */
+        sc_array_resize (inlist, inlist->elem_count + 1);
+        if ((size_t) si < incount - 1) {
+          memmove (sc_array_index (inlist, si + 2),
+                   sc_array_index (inlist, si + 1), incount - (si + 1));
+        }
+        q = p4est_quadrant_array_index (inlist, si + 1);
+        *q = tempp;
+        q->p.user_int = 0;
+        incount++;
+      }
+    }
+    else {
+      /* add tempp to inlist */
+      sc_array_resize (inlist, inlist->elem_count + 1);
+      memmove (sc_array_index (inlist, 1), sc_array_index (inlist, 0),
+               incount);
+      q = p4est_quadrant_array_index (inlist, 0);
+      *q = tempp;
+      q->p.user_int = 0;
+      incount++;
+    }
+  }
+
+  P4EST_ASSERT (sc_array_is_sorted (inlist, p4est_quadrant_compare));
+
+  /* initialize temporary storage */
+  for (l = 0; l <= minlevel; ++l) {
+    /* we don't need a hash table for minlevel, because all minlevel
+     * quadrants will be created automatically by filling in gaps */
+    hash[l] = NULL;
+    memset (&outlist[l], -1, sizeof (sc_array_t));
+  }
+  for (; l < maxlevel; ++l) {
+    hash[l] = sc_hash_new (p4est_quadrant_hash_fn, p4est_quadrant_equal_fn,
+                           NULL, list_alloc);
+    sc_array_init (&outlist[l], sizeof (p4est_quadrant_t *));
+  }
+  for (; l <= P4EST_MAXLEVEL; ++l) {
+    /* we don't need a hash table for maxlevel because a quad only spawns
+     * larger quads */
+    hash[l] = NULL;
+    memset (&outlist[l], -1, sizeof (sc_array_t));
+  }
+  outlist[maxlevel].elem_count = 0;
+
+  /* walk through the input tree bottom-up */
+  ph = 0;
+  pid = -1;
+  qalloc = p4est_quadrant_mempool_alloc (qpool);
+  qalloc->p.user_int = 0;
+
+  /* we don't need to run for minlevel + 1, because all of the quads that
+   * would be created would be outside dom */
+  for (l = maxlevel; l > minlevel + 1; l--) {
+    ocount = outlist[l].elem_count;     /* ocount is not growing */
+    olist = &outlist[l - 1];
+    for (jz = 0; jz < incount + ocount; ++jz) {
+      if (jz < incount) {
+        q = p4est_quadrant_array_index (inlist, jz);
+        if ((int) q->level != l || (q->p.user_int & duplicate)) {
+          /* if a duplicate, don't run */
+          continue;
+        }
+      }
+      else {
+        qpointer =
+          (p4est_quadrant_t **) sc_array_index (&outlist[l], jz - incount);
+        q = *qpointer;
+        P4EST_ASSERT ((int) q->level == l);
+      }
+      P4EST_ASSERT (p4est_quadrant_is_ancestor (dom, q));
+      P4EST_ASSERT (p4est_quadrant_child_id (q) == 0);
+
+      p4est_quadrant_parent (q, &par);  /* get the parent */
+      ph = P4EST_QUADRANT_LEN (par.level - 1);  /* twice its size */
+      pid = p4est_quadrant_child_id (&par);     /* and position */
+      p4est_quadrant_sibling (&par, &par, 0);   /* now shift to 0 */
+
+      for (sid = 0; sid < bound; sid++) {
+        *qalloc = par;
+        if (!sid) {
+          qalloc->p.user_int = precluded;
+          P4EST_ASSERT (p4est_quadrant_is_ancestor (dom, qalloc));
+        }
+        else if (sid <= P4EST_DIM) {
+          /* include face neighbors */
+          switch (sid - 1) {
+          case 0:
+            qalloc->x += ((pid & 1) ? ph : -ph);
+            break;
+          case 1:
+            qalloc->y += ((pid & 2) ? ph : -ph);
+            break;
+#ifdef P4_TO_P8
+          case 2:
+            qalloc->z += ((pid & 4) ? ph : -ph);
+            break;
+#endif
+          default:
+            SC_ABORT_NOT_REACHED ();
+          }
+        }
+#ifdef P4_TO_P8
+        else if (sid < 7) {
+          /* include edge neighbors */
+          switch (sid - 4) {
+          case 0:
+            qalloc->y += ((pid & 2) ? ph : -ph);
+            qalloc->z += ((pid & 4) ? ph : -ph);
+            break;
+          case 1:
+            qalloc->x += ((pid & 1) ? ph : -ph);
+            qalloc->z += ((pid & 4) ? ph : -ph);
+            break;
+          case 2:
+            qalloc->x += ((pid & 1) ? ph : -ph);
+            qalloc->y += ((pid & 2) ? ph : -ph);
+            break;
+          default:
+            SC_ABORT_NOT_REACHED ();
+          }
+        }
+#endif
+        else {
+          /* include corner neighbor */
+          qalloc->x += ((pid & 1) ? ph : -ph);
+          qalloc->y += ((pid & 2) ? ph : -ph);
+#ifdef P4_TO_P8
+          qalloc->z += ((pid & 4) ? ph : -ph);
+#endif
+        }
+
+        P4EST_ASSERT (p4est_quadrant_is_extended (qalloc));
+        P4EST_ASSERT (p4est_quadrant_child_id (qalloc) == 0);
+        P4EST_ASSERT (!sid || qalloc->p.user_int == 0);
+        P4EST_ASSERT (qalloc->level == l - 1);
+
+        /* do not add quadrants outside of the domain */
+        if (sid && !p4est_quadrant_is_ancestor (dom, qalloc)) {
+          continue;
+        }
+
+        /* make sure that qalloc is not included more than once */
+        inserted = sc_hash_insert_unique (hash[l - 1], qalloc, &vlookup);
+        if (!inserted) {
+          /* qalloc is already included in output list, this catches most */
+          ++count_already_outlist;
+          if (!sid) {
+            /* we need to relay the fact that this octant is precluded */
+            qlookup = (p4est_quadrant_t *) * vlookup;
+            qlookup->p.user_int = precluded;
+          }
+          continue;
+        }
+
+        if (sid) {
+          /* we do not need to search if we are adding the parent sibling: we
+           * already know that the octant is precluded, and any other octant
+           * we might find should already be marked duplicate */
+          srindex = sc_array_bsearch (inlist, qalloc,
+                                      p4est_quadrant_disjoint_parent);
+
+          if (srindex != -1) {
+            r = p4est_quadrant_array_index (inlist, srindex);
+
+            if (r->level >= l - 1) {
+              /* either qalloc duplicates r or is precluded by r: either way,
+               * we do not need to add qalloc to inlist in the final merge */
+              qalloc->p.user_int = precluded;
+              if (r->level > l - 1) {
+                ++count_ancestor_inlist;
+              }
+              else {
+                ++count_already_inlist;
+              }
+            }
+            if (r->level <= l - 1) {
+              /* either qalloc duplicates r, or an octant that can be traced to
+               * qalloc will duplicate r */
+              r->p.user_int |= duplicate;
+              if (r->level < l - 1) {
+                /* if qalloc precluded r, we can remove r before the final
+                 * merge */
+                r->p.user_int |= precluded;
+              }
+            }
+          }
+        }
+
+        qpointer = (p4est_quadrant_t **) sc_array_push (olist);
+        *qpointer = qalloc;
+        /* we need a new quadrant now, the old one is stored away */
+        qalloc = p4est_quadrant_mempool_alloc (qpool);
+        qalloc->p.user_int = 0;
+      }
+    }
+  }
+  sc_mempool_free (qpool, qalloc);
+
+  /* remove unneeded octants */
+  jz = 0;
+  for (iz = 0; iz < incount; iz++) {
+    q = p4est_quadrant_array_index (inlist, iz);
+    if ((q->p.user_int & precluded) == 0) {
+      if (jz != iz) {
+        p = p4est_quadrant_array_index (inlist, jz++);
+        *p = *q;
+      }
+      else {
+        jz++;
+      }
+    }
+  }
+  sc_array_resize (inlist, jz);
+  incount = jz;
+
+  for (l = minlevel + 1; l < maxlevel; ++l) {
+    /* print statistics and free hash tables */
+#ifdef P4EST_ENABLE_DEBUG
+    sc_hash_print_statistics (p4est_package_id, SC_LP_DEBUG, hash[l]);
+#endif
+    sc_hash_unlink_destroy (hash[l]);
+
+    /* merge valid quadrants from outlist into inlist */
+    ocount = outlist[l].elem_count;
+    q = NULL;
+    for (jz = 0; jz < ocount; ++jz) {
+      /* go through output list */
+      qpointer = (p4est_quadrant_t **) sc_array_index (&outlist[l], jz);
+      qalloc = *qpointer;
+      P4EST_ASSERT ((int) qalloc->level == l);
+      P4EST_ASSERT (p4est_quadrant_is_ancestor (dom, qalloc));
+      P4EST_ASSERT (p4est_quadrant_child_id (qalloc) == 0);
+      /* copy temporary quadrant into inlist */
+      if (first_desc != NULL && p4est_quadrant_compare (qalloc, &fd) < 0) {
+        sc_mempool_free (qpool, qalloc);
+        continue;
+      }
+      if (last_desc != NULL && p4est_quadrant_compare (qalloc, last_desc) > 0) {
+        sc_mempool_free (qpool, qalloc);
+        continue;
+      }
+      if (qalloc->p.user_int != precluded) {
+        q = p4est_quadrant_array_push (inlist);
+        *q = *qalloc;
+      }
+      sc_mempool_free (qpool, qalloc);
+    }
+    sc_array_reset (&outlist[l]);
+  }
+  P4EST_ASSERT (quadrant_pool_size == qpool->elem_count);
+  sc_mempool_truncate (list_alloc);
+
+  /* sort inlist */
+  if (inlist->elem_count > incount) {
+    sc_array_sort (inlist, p4est_quadrant_compare);
+  }
+
+  /* step through inlist and fill in the gaps in out */
+  /* note: we add to the end of out */
+  ocount = out->elem_count;
+
+  incount = inlist->elem_count;
+
+  tempq = fd;
+  if (first_desc == NULL) {
+    pid = 0;
+    jz = 0;
+  }
+  else {
+    /* find the first quadrant after tempq */
+    pid = p4est_quadrant_child_id (&tempq);
+    si = p4est_find_lower_bound (inlist, &tempq, 0);
+    if (si >= 0) {
+      jz = si;
+    }
+    else {
+      jz = incount;
+    }
+  }
+  if (jz < incount) {
+    q = p4est_quadrant_array_index (inlist, jz);
+    P4EST_ASSERT (p4est_quadrant_child_id (q) == 0);
+  }
+  else if (last_desc != NULL) {
+    p4est_quadrant_last_descendant (dom, &ld, P4EST_QMAXLEVEL);
+    if (p4est_quadrant_is_equal (&ld, last_desc)) {
+      q = NULL;
+    }
+    else {
+      lid = p4est_quadrant_linear_id (last_desc, P4EST_QMAXLEVEL);
+      lid++;
+      p4est_quadrant_set_morton (&ld, P4EST_QMAXLEVEL, lid);
+      P4EST_ASSERT (p4est_quadrant_is_ancestor (dom, &ld));
+      q = &ld;
+    }
+  }
+  else {
+    q = NULL;
+  }
+  for (;;) {
+    /* while tempq comes before q */
+    while (q == NULL || (!p4est_quadrant_is_equal (&tempq, q) &&
+                         !p4est_quadrant_is_ancestor (&tempq, q))) {
+      P4EST_ASSERT (q == NULL || p4est_quadrant_compare (&tempq, q) < 0);
+
+      /* stop once we're past last_desc */
+      if (last_desc != NULL && p4est_quadrant_compare (&tempq, last_desc) > 0) {
+        break;
+      }
+      /* add tempq to out */
+      r = (p4est_quadrant_t *) sc_array_push (out);
+      *r = tempq;
+
+      /* if tempq is a last sibling, go up a level */
+      while (tempq.level >= minlevel && pid == P4EST_CHILDREN - 1) {
+        p4est_quadrant_parent (&tempq, &tempp);
+        tempq = tempp;
+        pid = p4est_quadrant_child_id (&tempq);
+      }
+
+      /* if we've finished with all minlevel and smaller quadrants, we've
+       * filled dom */
+      if (tempq.level < minlevel) {
+        break;
+      }
+
+      /* get the next sibling */
+      p4est_quadrant_sibling (&tempq, &tempq, ++pid);
+    }
+
+    /* if we've finished with all minlevel and smaller quadrants, we've
+     * filled dom. also stop if we're past last_desc */
+    if (tempq.level < minlevel ||
+        (last_desc != NULL &&
+         p4est_quadrant_compare (&tempq, last_desc) > 0)) {
+      break;
+    }
+
+    P4EST_ASSERT (q != NULL);
+    P4EST_ASSERT (p4est_quadrant_is_equal (&tempq, q) ||
+                  p4est_quadrant_is_ancestor (&tempq, q));
+
+    if (q->x == tempq.x && q->y == tempq.y &&
+#ifdef P4_TO_P8
+        q->z == tempq.z &&
+#endif
+        1) {
+      /* if q is the first descendant of tempq, set tempq = q and get the next
+       * q */
+      if (q->level > tempq.level) {
+        pid = 0;
+      }
+      tempq.level = q->level;
+      jz++;
+      if (jz < incount) {
+        q = p4est_quadrant_array_index (inlist, jz);
+        P4EST_ASSERT (p4est_quadrant_child_id (q) == 0);
+      }
+      else if (last_desc != NULL) {
+        p4est_quadrant_last_descendant (dom, &ld, P4EST_QMAXLEVEL);
+        if (p4est_quadrant_is_equal (&ld, last_desc)) {
+          q = NULL;
+        }
+        else {
+          lid = p4est_quadrant_linear_id (last_desc, P4EST_QMAXLEVEL);
+          lid++;
+          p4est_quadrant_set_morton (&ld, P4EST_QMAXLEVEL, lid);
+          P4EST_ASSERT (p4est_quadrant_is_ancestor (dom, &ld));
+          q = &ld;
+        }
+      }
+      else {
+        q = NULL;
+      }
+    }
+    else {
+      /* get the largest first descendant of tempq that comes before
+       * q */
+      p4est_quadrant_first_descendant (&tempq, &tempp, P4EST_QMAXLEVEL);
+      p4est_nearest_common_ancestor (&tempp, q, &tempq);
+      tempq.level++;
+      pid = 0;
+      P4EST_ASSERT (p4est_quadrant_is_valid (&tempq));
+      P4EST_ASSERT (p4est_quadrant_compare (&tempq, q) < 0);
+      P4EST_ASSERT (!p4est_quadrant_is_ancestor (&tempq, q));
+    }
+  }
+
+#ifdef P4EST_ENABLE_DEBUG
+  sc_array_init_view (&outview, out, ocount, out->elem_count - ocount);
+  P4EST_ASSERT (sc_array_is_sorted (&outview, p4est_quadrant_compare));
+  P4EST_ASSERT (outview.elem_count > 1);
+
+  for (jz = 0; jz < outview.elem_count - 1; jz++) {
+    q = p4est_quadrant_array_index (&outview, jz);
+    r = p4est_quadrant_array_index (&outview, jz + 1);
+    P4EST_ASSERT (p4est_quadrant_is_next (q, r));
+  }
+#endif
+
+  if (count_in != NULL) {
+    *count_in += count_already_inlist;
+  }
+  if (count_out != NULL) {
+    *count_out += count_already_outlist;
+  }
+  if (count_an != NULL) {
+    *count_an += count_ancestor_inlist;
+  }
+
+}
+
+static void
+p4est_balance_replace_recursive (p4est_t * p4est, p4est_topidx_t nt,
+                                 sc_array_t * array, size_t start, size_t end,
+                                 p4est_quadrant_t * parent,
+                                 p4est_init_t init_fn,
+                                 p4est_replace_t replace_fn)
+{
+  p4est_quadrant_t    fam[P4EST_CHILDREN];
+  p4est_quadrant_t   *famp[P4EST_CHILDREN];
+  sc_array_t          view;
+  size_t              jz;
+  size_t              iz[P4EST_CHILDREN + 1];
+
+  if (end - start == P4EST_CHILDREN) {
+    for (jz = 0; jz < P4EST_CHILDREN; jz++) {
+      famp[jz] = p4est_quadrant_array_index (array, start + jz);
+    }
+    P4EST_ASSERT (p4est_quadrant_is_familypv (famp));
+    replace_fn (p4est, nt, 1, &parent, P4EST_CHILDREN, famp);
+    p4est_quadrant_free_data (p4est, parent);
+    return;
+  }
+  sc_array_init_view (&view, array, start, end - start);
+  p4est_split_array (&view, parent->level, iz);
+
+  for (jz = 0; jz < P4EST_CHILDREN; jz++) {
+    if (iz[jz] + 1 == iz[jz + 1]) {
+      famp[jz] = p4est_quadrant_array_index (array, start + iz[jz]);
+      P4EST_ASSERT (p4est_quadrant_is_parent (parent, famp[jz]));
+      P4EST_ASSERT (p4est_quadrant_child_id (famp[jz]) == (int) jz);
+    }
+    else {
+      fam[jz] = *parent;
+      famp[jz] = &fam[jz];
+      famp[jz]->level++;
+      p4est_quadrant_sibling (famp[jz], famp[jz], (int) jz);
+      p4est_quadrant_init_data (p4est, nt, famp[jz], init_fn);
+    }
+  }
+  replace_fn (p4est, nt, 1, &parent, P4EST_CHILDREN, famp);
+  p4est_quadrant_free_data (p4est, parent);
+
+  for (jz = 0; jz < P4EST_CHILDREN; jz++) {
+    if (famp[jz] == &fam[jz]) {
+      p4est_balance_replace_recursive (p4est, nt, array, start + iz[jz],
+                                       start + iz[jz + 1], famp[jz],
+                                       init_fn, replace_fn);
+    }
+  }
+}
+
+static void
+p4est_complete_or_balance (p4est_t * p4est, p4est_topidx_t which_tree,
+                           p4est_init_t init_fn, p4est_replace_t replace_fn,
+                           int btype)
+{
+  p4est_tree_t       *tree;
+  sc_array_t         *tquadrants;
+  int                 bound;
+  sc_mempool_t       *qpool;
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              data_pool_size;
+#endif
+  size_t              tcount;
+  size_t              count_already_inlist, count_already_outlist;
+  size_t              count_ancestor_inlist;
+  p4est_quadrant_t   *q, *p;
+  sc_mempool_t       *list_alloc;
+  sc_array_t         *inlist, *outlist;
+  size_t              iz, jz, jzstart = 0, jzend, ocount;
+  p4est_quadrant_t    tempq, root;
+
+  P4EST_ASSERT (which_tree >= p4est->first_local_tree);
+  P4EST_ASSERT (which_tree <= p4est->last_local_tree);
+  tree = p4est_tree_array_index (p4est->trees, which_tree);
+  tquadrants = &(tree->quadrants);
+
+  P4EST_ASSERT (0 <= btype && btype <= P4EST_DIM);
+  P4EST_ASSERT (sc_array_is_sorted (tquadrants, p4est_quadrant_compare));
+
+  switch (btype) {
+  case 0:
+    bound = 1;
+    break;
+  case 1:
+    bound = P4EST_DIM + 1;
+    break;
+  case P4EST_DIM:
+    bound = (1 << P4EST_DIM);
+    break;
+#ifdef P4_TO_P8
+  case 2:
+    bound = 7;
+    break;
+#endif
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+
+  qpool = p4est->quadrant_pool;
+
+#ifdef P4EST_ENABLE_DEBUG
+  data_pool_size = 0;
+  if (p4est->user_data_pool != NULL) {
+    data_pool_size = p4est->user_data_pool->elem_count;
+  }
+#endif
+
+  tcount = tquadrants->elem_count;
+  /* if tree is empty, there is nothing to do */
+  if (!tcount) {
+    return;
+  }
+
+  /* initialize some counters */
+  count_already_inlist = count_already_outlist = 0;
+  count_ancestor_inlist = 0;
+
+  /* get containing quadrant */
+  P4EST_QUADRANT_INIT (&root);
+  p4est_nearest_common_ancestor (&tree->first_desc, &tree->last_desc, &root);
+
+  if (tcount == 1) {
+    p = p4est_quadrant_array_index (tquadrants, 0);
+    if (p4est_quadrant_is_equal (p, &root)) {
+      /* nothing to be done, clean up and exit */
+      return;
+    }
+  }
+
+  /* initialize temporary storage */
+  list_alloc = sc_mempool_new (sizeof (sc_link_t));
+
+  inlist = sc_array_new (sizeof (p4est_quadrant_t));
+  outlist = sc_array_new (sizeof (p4est_quadrant_t));
+
+  /* get the reduced representation of the tree */
+  q = (p4est_quadrant_t *) sc_array_push (inlist);
+  p = p4est_quadrant_array_index (tquadrants, 0);
+  p4est_quadrant_sibling (p, q, 0);
+  for (iz = 1; iz < tcount; iz++) {
+    p = p4est_quadrant_array_index (tquadrants, iz);
+    P4EST_ASSERT (p4est_quadrant_is_ancestor (&root, p));
+    p4est_nearest_common_ancestor (p, q, &tempq);
+    if (tempq.level >= SC_MIN (q->level, p->level) - 1) {
+      if (p->level > q->level) {
+        *q = *p;
+      }
+      continue;
+    }
+    q = (p4est_quadrant_t *) sc_array_push (inlist);
+    p4est_quadrant_sibling (p, q, 0);
+  }
+
+  /* balance */
+  p4est_complete_or_balance_kernel (inlist, &root, bound, qpool,
+                                    list_alloc, outlist,
+                                    &(tree->first_desc),
+                                    &(tree->last_desc),
+                                    &count_already_inlist,
+                                    &count_already_outlist,
+                                    &count_ancestor_inlist);
+
+  ocount = outlist->elem_count;
+
+  iz = 0;                       /* tquadrants */
+  jz = 0;                       /* outlist */
+
+  /* initialize quadrants in outlist */
+  while (iz < tcount && jz < ocount) {
+    q = p4est_quadrant_array_index (tquadrants, iz);
+    p = p4est_quadrant_array_index (outlist, jz);
+
+    /* watch out for gaps in tquadrants */
+    while (p4est_quadrant_compare (p, q) < 0) {
+      P4EST_ASSERT (!p4est_quadrant_is_ancestor (p, q));
+      ++tree->quadrants_per_level[p->level];
+      p4est_quadrant_init_data (p4est, which_tree, p, init_fn);
+      jz++;
+      P4EST_ASSERT (jz < ocount);
+      p = p4est_quadrant_array_index (outlist, jz);
+    }
+
+    /* watchout out for tquadrants that have been split */
+    if (q->level < p->level) {
+      P4EST_ASSERT (p4est_quadrant_is_ancestor (q, p));
+      /* reset q */
+      --tree->quadrants_per_level[q->level];
+      if (replace_fn == NULL) {
+        p4est_quadrant_free_data (p4est, q);
+      }
+      else {
+        tempq = *q;
+        jzstart = jz;
+      }
+      while (jz < ocount && p4est_quadrant_is_ancestor (q, p)) {
+        ++tree->quadrants_per_level[p->level];
+        p4est_quadrant_init_data (p4est, which_tree, p, init_fn);
+        if (++jz < ocount) {
+          p = p4est_quadrant_array_index (outlist, jz);
+        }
+      }
+      if (replace_fn != NULL) {
+        jzend = jz;
+        p4est_balance_replace_recursive (p4est, which_tree,
+                                         outlist, jzstart, jzend, &tempq,
+                                         init_fn, replace_fn);
+      }
+      iz++;
+    }
+    else {
+      P4EST_ASSERT (p4est_quadrant_is_equal (q, p));
+      p->p.user_data = q->p.user_data;
+      iz++;
+      jz++;
+    }
+  }
+
+  P4EST_ASSERT (iz == tcount);
+
+  /* initialize new quadrants after last tquadrant */
+  for (; jz < ocount; jz++) {
+    p = p4est_quadrant_array_index (outlist, jz);
+    ++tree->quadrants_per_level[p->level];
+    p4est_quadrant_init_data (p4est, which_tree, p, init_fn);
+  }
+
+  /* resize tquadrants and copy */
+  sc_array_resize (tquadrants, ocount);
+  memcpy (tquadrants->array, outlist->array, outlist->elem_size * ocount);
+
+  /* sanity check */
+  if (p4est->user_data_pool != NULL) {
+    P4EST_ASSERT (data_pool_size + (ocount - tcount) ==
+                  p4est->user_data_pool->elem_count);
+  }
+
+  P4EST_VERBOSEF
+    ("Tree %lld inlist %llu outlist %llu ancestor %llu insert %llu\n",
+     (long long) which_tree, (unsigned long long) count_already_inlist,
+     (unsigned long long) count_already_outlist,
+     (unsigned long long) count_ancestor_inlist,
+     (unsigned long long) (ocount - tcount));
+
+  sc_array_destroy (inlist);
+  sc_array_destroy (outlist);
+  sc_mempool_destroy (list_alloc);
+
+  if (p4est->inspect) {
+    if (!p4est->inspect->use_B) {
+      p4est->inspect->balance_A_count_in += count_already_inlist;
+      p4est->inspect->balance_A_count_in += count_ancestor_inlist;
+      p4est->inspect->balance_A_count_out += count_already_outlist;
+    }
+    else {
+      p4est->inspect->balance_B_count_in += count_already_inlist;
+      p4est->inspect->balance_B_count_in += count_ancestor_inlist;
+      p4est->inspect->balance_B_count_out += count_already_outlist;
+    }
+  }
+}
+
+void
+p4est_balance_border (p4est_t * p4est, p4est_connect_type_t btype,
+                      p4est_topidx_t which_tree, p4est_init_t init_fn,
+                      p4est_replace_t replace_fn, sc_array_t * borders)
+{
+  size_t              iz, jz, kz;
+  size_t              incount;
+  size_t              count_already_inlist, count_already_outlist;
+  size_t              count_ancestor_inlist;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *q, *p, *r;
+  p4est_quadrant_t    tempq, tempp;
+  sc_array_t          qview;
+  sc_array_t         *inlist, *flist, *tquadrants;
+  sc_array_t          tqview;
+  size_t              tqoffset, fcount;
+  p4est_topidx_t      first_tree = p4est->first_local_tree;
+  size_t              num_added, num_this_added;
+  int                 bound;
+  ssize_t             tqindex;
+  size_t              tqorig;
+  sc_mempool_t       *list_alloc, *qpool;
+  /* get this tree's border */
+  sc_array_t         *qarray = (sc_array_t *) sc_array_index (borders,
+                                                              which_tree -
+                                                              first_tree);
+  size_t              qcount = qarray->elem_count;
+
+  if (!qcount) {
+    /* nothing to be done */
+    return;
+  }
+
+  P4EST_QUADRANT_INIT (&tempq);
+  P4EST_QUADRANT_INIT (&tempp);
+
+  /* set up balance machinery */
+
+  if (btype == P4EST_CONNECT_FULL) {
+    bound = (1 << P4EST_DIM);
+  }
+#ifdef P4_TO_P8
+  else if (btype == P8EST_CONNECT_EDGE) {
+    bound = (1 << P4EST_DIM) - 1;
+  }
+#endif
+  else {
+    bound = P4EST_DIM + 1;
+  }
+
+  P4EST_ASSERT (which_tree >= p4est->first_local_tree);
+  P4EST_ASSERT (which_tree <= p4est->last_local_tree);
+
+  tree = p4est_tree_array_index (p4est->trees, which_tree);
+  tquadrants = &(tree->quadrants);
+  tqorig = tquadrants->elem_count;
+  tqoffset = 0;
+  sc_array_init_view (&tqview, tquadrants, tqoffset,
+                      tquadrants->elem_count - tqoffset);
+
+  qpool = p4est->quadrant_pool;
+
+  count_already_inlist = count_already_outlist = 0;
+  count_ancestor_inlist = 0;
+  num_added = 0;
+
+  /* initialize temporary storage */
+  list_alloc = sc_mempool_new (sizeof (sc_link_t));
+
+  inlist = sc_array_new (sizeof (p4est_quadrant_t));
+  flist = sc_array_new (sizeof (p4est_quadrant_t));
+
+  /* sort the border and remove duplicates */
+  sc_array_sort (qarray, p4est_quadrant_compare);
+  jz = 1;                       /* number included */
+  kz = 0;                       /* number skipped */
+  p = p4est_quadrant_array_index (qarray, 0);
+  P4EST_ASSERT (p4est_quadrant_is_valid (p));
+  for (iz = 1; iz < qcount; iz++) {
+    q = p4est_quadrant_array_index (qarray, iz);
+    P4EST_ASSERT (p4est_quadrant_is_extended (q));
+    if (!p4est_quadrant_is_equal (q, p)) {
+      p++;
+      jz++;
+      if (kz) {
+        *p = *q;
+      }
+    }
+    else {
+      kz++;
+    }
+  }
+  P4EST_ASSERT (kz + jz == qcount);
+  sc_array_resize (qarray, jz);
+  qcount = jz;
+
+  /* step through border */
+  for (iz = 0; iz < qcount; iz++) {
+    p = p4est_quadrant_array_index (qarray, iz);
+
+    if (p4est_quadrant_compare (p, &(tree->first_desc)) < 0 &&
+        !p4est_quadrant_is_ancestor (p, &(tree->first_desc))) {
+      continue;
+    }
+    if (p4est_quadrant_compare (p, &(tree->last_desc)) > 0) {
+      continue;
+    }
+
+    P4EST_ASSERT (p4est_quadrant_is_valid (p));
+
+    /* get a view of all of the quads that are descended from this quad */
+    jz = iz + 1;
+    kz = jz;
+
+    if (kz < qcount) {
+      q = p4est_quadrant_array_index (qarray, kz);
+    }
+
+    while (kz < qcount && p4est_quadrant_is_ancestor (p, q)) {
+      kz++;
+
+      P4EST_ASSERT (p4est_quadrant_child_id (q) == 0);
+
+      if (kz < qcount) {
+        q = p4est_quadrant_array_index (qarray, kz);
+      }
+    }
+
+    incount = kz - jz;
+    if (!incount) {
+      continue;
+    }
+
+    /* find p in tquadrants */
+    tqindex = sc_array_bsearch (&tqview, p, p4est_quadrant_compare);
+
+    P4EST_ASSERT (tqindex >= 0);
+
+    /* copy everything before p into flist */
+    if (tqindex) {
+      fcount = flist->elem_count;
+      sc_array_resize (flist, fcount + tqindex);
+      memcpy (sc_array_index (flist, fcount),
+              tqview.array, tqindex * sizeof (p4est_quadrant_t));
+    }
+
+    /* update the view of tquadrants to be everything past p */
+    tqindex += tqoffset;        /* tqindex is the index of p in tquadrants */
+    tqoffset = tqindex + 1;
+    sc_array_init_view (&tqview, tquadrants, tqoffset, tqorig - tqoffset);
+
+    /* first, remove p */
+    q = p4est_quadrant_array_index (tquadrants, tqindex);
+    P4EST_ASSERT (p4est_quadrant_is_equal (q, p));
+    /* reset the data, decrement level count */
+    if (replace_fn == NULL) {
+      p4est_quadrant_free_data (p4est, q);
+    }
+    else {
+      tempp = *q;
+    }
+    --tree->quadrants_per_level[q->level];
+
+    /* get all of the quadrants that descend from p into inlist */
+    sc_array_init_view (&qview, qarray, jz, incount);
+    sc_array_resize (inlist, 1);
+    q = p4est_quadrant_array_index (inlist, 0);
+    r = p4est_quadrant_array_index (&qview, 0);
+    P4EST_ASSERT (p4est_quadrant_child_id (r) == 0);
+    *q = *r;
+    for (jz = 1; jz < incount; jz++) {
+      r = p4est_quadrant_array_index (&qview, jz);
+      P4EST_ASSERT (p4est_quadrant_child_id (r) == 0);
+      p4est_nearest_common_ancestor (r, q, &tempq);
+      if (tempq.level >= SC_MIN (r->level, q->level) - 1) {
+        if (r->level > q->level) {
+          *q = *r;
+        }
+        continue;
+      }
+      q = (p4est_quadrant_t *) sc_array_push (inlist);
+      *q = *r;
+    }
+
+    fcount = flist->elem_count;
+
+    /* balance them within the containing quad */
+    p4est_complete_or_balance_kernel (inlist, p, bound, qpool, list_alloc,
+                                      flist, NULL, NULL,
+                                      &count_already_inlist,
+                                      &count_already_outlist,
+                                      &count_ancestor_inlist);
+
+    /* count the amount we've added (-1 because we subtract p) */
+    num_this_added = flist->elem_count - 1 - fcount;
+    num_added += num_this_added;
+
+    /* initialize */
+    for (jz = fcount; jz < flist->elem_count; jz++) {
+      q = p4est_quadrant_array_index (flist, jz);
+      P4EST_ASSERT (p4est_quadrant_is_ancestor (p, q));
+      ++tree->quadrants_per_level[q->level];
+      tree->maxlevel = (int8_t) SC_MAX (tree->maxlevel, q->level);
+      p4est_quadrant_init_data (p4est, which_tree, q, init_fn);
+    }
+    if (replace_fn != NULL) {
+      p4est_balance_replace_recursive (p4est, which_tree,
+                                       flist, fcount, flist->elem_count,
+                                       &tempp, init_fn, replace_fn);
+    }
+
+    /* skip over the quadrants that we just operated on */
+    iz = kz - 1;
+  }
+
+  /* copy the remaining tquadrants to flist */
+  if (tqoffset < tqorig) {
+    fcount = flist->elem_count;
+    sc_array_resize (flist, fcount + tqorig - tqoffset);
+    memcpy (sc_array_index (flist, fcount),
+            tqview.array, (tqorig - tqoffset) * sizeof (p4est_quadrant_t));
+  }
+
+  /* copy flist into tquadrants */
+  sc_array_resize (tquadrants, flist->elem_count);
+  memcpy (tquadrants->array, flist->array,
+          flist->elem_count * flist->elem_size);
+
+  sc_mempool_destroy (list_alloc);
+  P4EST_ASSERT (tqorig + num_added == tquadrants->elem_count);
+
+  /* print more statistics */
+  P4EST_VERBOSEF
+    ("Tree border %lld inlist %llu outlist %llu ancestor %llu insert %llu\n",
+     (long long) which_tree, (unsigned long long) count_already_inlist,
+     (unsigned long long) count_already_outlist,
+     (unsigned long long) count_ancestor_inlist,
+     (unsigned long long) num_added);
+
+  sc_array_destroy (inlist);
+  sc_array_destroy (flist);
+
+  P4EST_ASSERT (p4est_tree_is_complete (tree));
+
+  if (p4est->inspect) {
+    p4est->inspect->balance_B_count_in += count_already_inlist;
+    p4est->inspect->balance_B_count_in += count_ancestor_inlist;
+    p4est->inspect->balance_B_count_out += count_already_outlist;
+  }
+}
+
+void
+p4est_complete_subtree (p4est_t * p4est,
+                        p4est_topidx_t which_tree, p4est_init_t init_fn)
+{
+  p4est_complete_or_balance (p4est, which_tree, init_fn, NULL, 0);
+}
+
+void
+p4est_balance_subtree (p4est_t * p4est, p4est_connect_type_t btype,
+                       p4est_topidx_t which_tree, p4est_init_t init_fn)
+{
+  p4est_complete_or_balance (p4est, which_tree, init_fn, NULL,
+                             p4est_connect_type_int (btype));
+}
+
+void
+p4est_balance_subtree_ext (p4est_t * p4est, p4est_connect_type_t btype,
+                           p4est_topidx_t which_tree, p4est_init_t init_fn,
+                           p4est_replace_t replace_fn)
+{
+  p4est_complete_or_balance (p4est, which_tree, init_fn, replace_fn,
+                             p4est_connect_type_int (btype));
+}
+
+size_t
+p4est_linearize_tree (p4est_t * p4est, p4est_tree_t * tree)
+{
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              data_pool_size;
+#endif
+  size_t              incount, removed;
+  size_t              current, rest;
+  p4est_locidx_t      num_quadrants;
+  int                 i, maxlevel;
+  p4est_quadrant_t   *q1, *q2;
+  sc_array_t         *tquadrants = &tree->quadrants;
+
+  P4EST_ASSERT (sc_array_is_sorted (tquadrants, p4est_quadrant_compare));
+
+  incount = tquadrants->elem_count;
+  if (incount <= 1) {
+    return 0;
+  }
+#ifdef P4EST_ENABLE_DEBUG
+  data_pool_size = 0;
+  if (p4est->user_data_pool != NULL) {
+    data_pool_size = p4est->user_data_pool->elem_count;
+  }
+#endif
+  removed = 0;
+
+  /* run through the array and remove ancestors */
+  current = 0;
+  rest = current + 1;
+  q1 = p4est_quadrant_array_index (tquadrants, current);
+  while (rest < incount) {
+    q2 = p4est_quadrant_array_index (tquadrants, rest);
+    if (p4est_quadrant_is_equal (q1, q2) ||
+        p4est_quadrant_is_ancestor (q1, q2)) {
+      --tree->quadrants_per_level[q1->level];
+      p4est_quadrant_free_data (p4est, q1);
+      *q1 = *q2;
+      ++removed;
+      ++rest;
+    }
+    else {
+      ++current;
+      if (current < rest) {
+        q1 = p4est_quadrant_array_index (tquadrants, current);
+        *q1 = *q2;
+      }
+      else {
+        q1 = q2;
+      }
+      ++rest;
+    }
+  }
+
+  /* resize array */
+  sc_array_resize (tquadrants, current + 1);
+
+  /* update level counters */
+  maxlevel = 0;
+  num_quadrants = 0;
+  for (i = 0; i <= P4EST_QMAXLEVEL; ++i) {
+    P4EST_ASSERT (tree->quadrants_per_level[i] >= 0);
+    num_quadrants += tree->quadrants_per_level[i];      /* same type */
+    if (tree->quadrants_per_level[i] > 0) {
+      maxlevel = i;
+    }
+  }
+  tree->maxlevel = (int8_t) maxlevel;
+
+  /* sanity checks */
+  P4EST_ASSERT (num_quadrants == (p4est_locidx_t) tquadrants->elem_count);
+  P4EST_ASSERT (tquadrants->elem_count == incount - removed);
+  if (p4est->user_data_pool != NULL) {
+    P4EST_ASSERT (data_pool_size - removed ==
+                  p4est->user_data_pool->elem_count);
+  }
+  P4EST_ASSERT (p4est_tree_is_sorted (tree));
+  P4EST_ASSERT (p4est_tree_is_linear (tree));
+
+  return removed;
+}
+
+p4est_locidx_t
+p4est_partition_correction (p4est_gloidx_t * partition,
+                            int num_procs, int rank,
+                            p4est_gloidx_t min_quadrant_id,
+                            p4est_gloidx_t max_quadrant_id)
+{
+  int                 i;
+  int                 rank_with_max_quads = rank;
+  p4est_gloidx_t      h;
+  p4est_gloidx_t      max_num_quadrants =
+    SC_MIN (max_quadrant_id, partition[rank + 1] - 1) - partition[rank] + 1;
+
+  /* no correction if num quadrants not sufficient for family */
+  if (max_quadrant_id - min_quadrant_id + 1 != P4EST_CHILDREN) {
+    return 0;
+  }
+
+  /* decreasing search for process with highest amount of quadrants */
+  i = rank_with_max_quads - 1;
+  while (min_quadrant_id < partition[i + 1]) {
+    h = partition[i + 1] - SC_MAX (min_quadrant_id, partition[i]);
+    if (max_num_quadrants <= h) {
+      max_num_quadrants = h;
+      rank_with_max_quads = i;
+    }
+    i--;
+  }
+
+  /* increasing search for process with highest amount of quadrants */
+  i = rank_with_max_quads + 1;
+  while (partition[i] <= max_quadrant_id) {
+    h = SC_MIN (max_quadrant_id, partition[i + 1] - 1) - partition[i] + 1;
+    if (max_num_quadrants < h) {
+      max_num_quadrants = h;
+      rank_with_max_quads = i;
+    }
+    i++;
+  }
+
+  /* compute correction */
+  if (rank_with_max_quads < rank) {
+    return (p4est_locidx_t) (partition[rank] - max_quadrant_id - 1);
+  }
+  else {
+    return (p4est_locidx_t) (partition[rank] - min_quadrant_id);
+  }
+}
+
+int
+p4est_next_nonempty_process (int rank, int num_procs,
+                             p4est_locidx_t * num_quadrants_in_proc)
+{
+  if (rank >= num_procs) {      /* if `rank` is too high */
+    /* return process id beyond scope */
+    return num_procs;
+  }
+
+  /* search for next non empty process */
+  while (rank < num_procs && num_quadrants_in_proc[rank] == 0) {
+    rank++;
+  }
+
+  /* return non empty process id or last process id respectively */
+  return rank;
+}
+
+p4est_gloidx_t
+p4est_partition_given (p4est_t * p4est,
+                       const p4est_locidx_t * new_num_quadrants_in_proc)
+{
+  const int           num_procs = p4est->mpisize;
+  const int           rank = p4est->mpirank;
+  const p4est_topidx_t first_local_tree = p4est->first_local_tree;
+  const p4est_topidx_t last_local_tree = p4est->last_local_tree;
+  const size_t        data_size = p4est->data_size;
+  const size_t        quad_plus_data_size = sizeof (p4est_quadrant_t)
+    + data_size;
+  sc_array_t         *trees = p4est->trees;
+
+  /* *INDENT-OFF* horrible indent bug */
+  const p4est_topidx_t num_send_trees =
+    p4est->global_first_position[rank + 1].p.which_tree - /* same type */
+    p4est->global_first_position[rank].p.which_tree + 1;
+  /* *INDENT-ON* */
+
+  int                 i, sk;
+  int                 from_proc, to_proc;
+  int                 num_proc_recv_from, num_proc_send_to;
+  char               *user_data_send_buf;
+  char               *user_data_recv_buf;
+  char              **recv_buf, **send_buf;
+  size_t              recv_size, send_size, zz, zoffset;
+  p4est_topidx_t      it;
+  p4est_topidx_t      which_tree;
+  p4est_topidx_t      first_tree, last_tree;
+  p4est_topidx_t      num_recv_trees;
+  p4est_topidx_t      new_first_local_tree, new_last_local_tree;
+  p4est_topidx_t      first_from_tree, last_from_tree, from_tree;
+  p4est_locidx_t      il;
+  p4est_locidx_t      num_copy;
+  p4est_locidx_t      num_quadrants;
+  p4est_locidx_t      new_local_num_quadrants;
+  p4est_locidx_t     *num_recv_from, *num_send_to;
+  p4est_locidx_t     *new_local_tree_elem_count;
+  p4est_locidx_t     *new_local_tree_elem_count_before;
+  p4est_locidx_t     *num_per_tree_local;
+  p4est_locidx_t     *num_per_tree_send_buf;
+  p4est_locidx_t     *num_per_tree_recv_buf;
+  p4est_gloidx_t     *begin_send_to;
+  p4est_gloidx_t      tree_from_begin, tree_from_end, num_copy_global;
+  p4est_gloidx_t      from_begin, from_end;
+  p4est_gloidx_t      to_begin, to_end;
+  p4est_gloidx_t      my_base, my_begin, my_end;
+  p4est_gloidx_t     *global_last_quad_index;
+  p4est_gloidx_t     *new_global_last_quad_index;
+  p4est_gloidx_t     *local_tree_last_quad_index;
+  p4est_gloidx_t      diff64, total_quadrants_shipped;
+  sc_array_t         *quadrants;
+  p4est_quadrant_t   *quad_send_buf;
+  p4est_quadrant_t   *quad_recv_buf;
+  p4est_quadrant_t   *quad;
+  p4est_tree_t       *tree;
+#ifdef P4EST_ENABLE_MPI
+  int                 mpiret;
+  MPI_Comm            comm = p4est->mpicomm;
+  MPI_Request        *recv_request, *send_request;
+#endif
+#ifdef P4EST_ENABLE_DEBUG
+  unsigned            crc;
+  p4est_gloidx_t      total_requested_quadrants = 0;
+#endif
+
+  P4EST_GLOBAL_INFOF
+    ("Into " P4EST_STRING "_partition_given with %lld total quadrants\n",
+     (long long) p4est->global_num_quadrants);
+
+#ifdef P4EST_ENABLE_DEBUG
+  /* Save a checksum of the original forest */
+  crc = p4est_checksum (p4est);
+#endif
+
+  /* Check for a valid requested partition and create last_quad_index */
+  global_last_quad_index = P4EST_ALLOC (p4est_gloidx_t, num_procs);
+  for (i = 0; i < num_procs; ++i) {
+    global_last_quad_index[i] = p4est->global_first_quadrant[i + 1] - 1;
+#ifdef P4EST_ENABLE_DEBUG
+    total_requested_quadrants += new_num_quadrants_in_proc[i];
+    P4EST_ASSERT (new_num_quadrants_in_proc[i] >= 0);
+#endif
+  }
+  P4EST_ASSERT (total_requested_quadrants == p4est->global_num_quadrants);
+
+  /* Print some diagnostics */
+  if (rank == 0) {
+    for (i = 0; i < num_procs; ++i) {
+      P4EST_GLOBAL_LDEBUGF ("partition global_last_quad_index[%d] = %lld\n",
+                            i, (long long) global_last_quad_index[i]);
+    }
+  }
+
+  /* Calculate the global_last_quad_index for the repartitioned forest */
+  new_global_last_quad_index = P4EST_ALLOC (p4est_gloidx_t, num_procs);
+  new_global_last_quad_index[0] = new_num_quadrants_in_proc[0] - 1;
+  for (i = 1; i < num_procs; ++i) {
+    new_global_last_quad_index[i] = new_num_quadrants_in_proc[i] +
+      new_global_last_quad_index[i - 1];
+  }
+  P4EST_ASSERT (global_last_quad_index[num_procs - 1] ==
+                new_global_last_quad_index[num_procs - 1]);
+
+  /* Calculate the global number of shipped (= received) quadrants */
+  total_quadrants_shipped = 0;
+  for (i = 1; i < num_procs; ++i) {
+    diff64 =
+      global_last_quad_index[i - 1] - new_global_last_quad_index[i - 1];
+    if (diff64 >= 0) {
+      total_quadrants_shipped +=
+        SC_MIN (diff64, new_num_quadrants_in_proc[i]);
+    }
+    else {
+      total_quadrants_shipped +=
+        SC_MIN (-diff64, new_num_quadrants_in_proc[i - 1]);
+    }
+  }
+  P4EST_ASSERT (0 <= total_quadrants_shipped &&
+                total_quadrants_shipped <= p4est->global_num_quadrants);
+
+  /* Print some diagnostics */
+  if (rank == 0) {
+    for (i = 0; i < num_procs; ++i) {
+      P4EST_GLOBAL_LDEBUGF
+        ("partition new_global_last_quad_index[%d] = %lld\n",
+         i, (long long) new_global_last_quad_index[i]);
+    }
+  }
+
+  /* Calculate the local index of the end of each tree */
+  local_tree_last_quad_index =
+    P4EST_ALLOC_ZERO (p4est_gloidx_t, trees->elem_count);
+  if (first_local_tree >= 0) {
+    tree = p4est_tree_array_index (trees, first_local_tree);
+    local_tree_last_quad_index[first_local_tree]
+      = tree->quadrants.elem_count - 1;
+  }
+  else {
+    /* empty processor */
+    P4EST_ASSERT (first_local_tree == -1 && last_local_tree == -2);
+  }
+  for (which_tree = first_local_tree + 1;       /* same type */
+       which_tree <= last_local_tree; ++which_tree) {
+    tree = p4est_tree_array_index (trees, which_tree);
+    local_tree_last_quad_index[which_tree] = tree->quadrants.elem_count
+      + local_tree_last_quad_index[which_tree - 1];
+  }
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (which_tree = first_local_tree; which_tree <= last_local_tree;
+       ++which_tree) {
+    tree = p4est_tree_array_index (trees, which_tree);
+    P4EST_LDEBUGF
+      ("partition tree %lld local_tree_last_quad_index[%lld] = %lld\n",
+       (long long) which_tree, (long long) which_tree,
+       (long long) local_tree_last_quad_index[which_tree]);
+  }
+#endif
+
+  /* Calculate where the new quadrants are coming from */
+  num_recv_from = P4EST_ALLOC_ZERO (p4est_locidx_t, num_procs);
+
+  my_begin = (rank == 0) ? 0 : (new_global_last_quad_index[rank - 1] + 1);
+  my_end = new_global_last_quad_index[rank];
+
+  num_proc_recv_from = 0;
+  for (from_proc = 0; from_proc < num_procs; ++from_proc) {
+    from_begin = (from_proc == 0) ?
+      0 : (global_last_quad_index[from_proc - 1] + 1);
+    from_end = global_last_quad_index[from_proc];
+
+    if (from_begin <= my_end && from_end >= my_begin) {
+      /* from_proc sends to me but may be empty */
+      num_recv_from[from_proc] = SC_MIN (my_end, from_end)
+        - SC_MAX (my_begin, from_begin) + 1;
+      P4EST_ASSERT (num_recv_from[from_proc] >= 0);
+      if (from_proc != rank)
+        ++num_proc_recv_from;
+    }
+  }
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (i = 0; i < num_procs; ++i) {
+    if (num_recv_from[i] != 0) {
+      P4EST_LDEBUGF ("partition num_recv_from[%d] = %lld\n", i,
+                     (long long) num_recv_from[i]);
+    }
+  }
+#endif
+
+  /* Post receives for the quadrants and their data */
+  recv_buf = P4EST_ALLOC_ZERO (char *, num_procs);
+#ifdef P4EST_ENABLE_MPI
+  recv_request = P4EST_ALLOC (MPI_Request, num_proc_recv_from);
+#endif
+
+  /* Allocate space for receiving quadrants and user data */
+  for (from_proc = 0, sk = 0; from_proc < num_procs; ++from_proc) {
+    if (from_proc != rank && num_recv_from[from_proc] > 0) {
+      num_recv_trees =          /* same type */
+        p4est->global_first_position[from_proc + 1].p.which_tree
+        - p4est->global_first_position[from_proc].p.which_tree + 1;
+      recv_size = num_recv_trees * sizeof (p4est_locidx_t)
+        + quad_plus_data_size * num_recv_from[from_proc];
+
+      recv_buf[from_proc] = P4EST_ALLOC (char, recv_size);
+
+      /* Post receives for the quadrants and their data */
+#ifdef P4EST_ENABLE_MPI
+      P4EST_LDEBUGF ("partition recv %lld quadrants from %d\n",
+                     (long long) num_recv_from[from_proc], from_proc);
+      mpiret = MPI_Irecv (recv_buf[from_proc], (int) recv_size, MPI_BYTE,
+                          from_proc, P4EST_COMM_PARTITION_GIVEN,
+                          comm, recv_request + sk);
+      SC_CHECK_MPI (mpiret);
+#endif
+      ++sk;
+    }
+  }
+#ifdef P4EST_ENABLE_MPI
+  for (; sk < num_proc_recv_from; ++sk) {
+    /* for empty processors in receiving range */
+    recv_request[sk] = MPI_REQUEST_NULL;
+  }
+#endif
+
+  /* For each processor calculate the number of quadrants sent */
+  num_send_to = P4EST_ALLOC (p4est_locidx_t, num_procs);
+  begin_send_to = P4EST_ALLOC (p4est_gloidx_t, num_procs);
+
+  my_begin = (rank == 0) ? 0 : (global_last_quad_index[rank - 1] + 1);
+  my_end = global_last_quad_index[rank];
+
+  num_proc_send_to = 0;
+  for (to_proc = 0; to_proc < num_procs; ++to_proc) {
+    to_begin = (to_proc == 0)
+      ? 0 : (new_global_last_quad_index[to_proc - 1] + 1);
+    to_end = new_global_last_quad_index[to_proc];
+
+    if (to_begin <= my_end && to_end >= my_begin) {
+      /* I send to to_proc which may be empty */
+      num_send_to[to_proc] = SC_MIN (my_end, to_end)
+        - SC_MAX (my_begin, to_begin) + 1;
+      begin_send_to[to_proc] = SC_MAX (my_begin, to_begin);
+      P4EST_ASSERT (num_send_to[to_proc] >= 0);
+      if (to_proc != rank)
+        ++num_proc_send_to;
+    }
+    else {
+      /* I don't send to to_proc */
+      num_send_to[to_proc] = 0;
+      begin_send_to[to_proc] = -1;
+    }
+  }
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (i = 0; i < num_procs; ++i) {
+    if (num_send_to[i] != 0) {
+      P4EST_LDEBUGF ("partition num_send_to[%d] = %lld\n",
+                     i, (long long) num_send_to[i]);
+    }
+  }
+  for (i = 0; i < num_procs; ++i) {
+    if (begin_send_to[i] != -1) {
+      P4EST_LDEBUGF ("partition begin_send_to[%d] = %lld\n",
+                     i, (long long) begin_send_to[i]);
+    }
+  }
+#endif
+
+  /* Communicate the quadrants and their data */
+  send_buf = P4EST_ALLOC (char *, num_procs);
+#ifdef P4EST_ENABLE_MPI
+  send_request = P4EST_ALLOC (MPI_Request, num_proc_send_to);
+#endif
+
+  /* Set the num_per_tree_local */
+  num_per_tree_local = P4EST_ALLOC_ZERO (p4est_locidx_t, num_send_trees);
+  to_proc = rank;
+  my_base = (rank == 0) ? 0 : (global_last_quad_index[rank - 1] + 1);
+  my_begin = begin_send_to[to_proc] - my_base;
+  my_end = begin_send_to[to_proc] + num_send_to[to_proc] - 1 - my_base;
+  for (which_tree = first_local_tree; which_tree <= last_local_tree;
+       ++which_tree) {
+    tree = p4est_tree_array_index (trees, which_tree);
+
+    from_begin = (which_tree == first_local_tree) ? 0 :
+      (local_tree_last_quad_index[which_tree - 1] + 1);
+    from_end = local_tree_last_quad_index[which_tree];
+
+    if (from_begin <= my_end && from_end >= my_begin) {
+      /* Need to copy from tree which_tree */
+      tree_from_begin = SC_MAX (my_begin, from_begin) - from_begin;
+      tree_from_end = SC_MIN (my_end, from_end) - from_begin;
+      num_copy_global = tree_from_end - tree_from_begin + 1;
+      P4EST_ASSERT (num_copy_global >= 0);
+      P4EST_ASSERT (num_copy_global <= (p4est_gloidx_t) P4EST_LOCIDX_MAX);
+      num_copy = (p4est_locidx_t) num_copy_global;
+      num_per_tree_local[which_tree - first_local_tree] = num_copy;
+    }
+  }
+
+  /* Allocate space for receiving quadrants and user data */
+  for (to_proc = 0, sk = 0; to_proc < num_procs; ++to_proc) {
+    if (to_proc != rank && num_send_to[to_proc]) {
+      send_size = num_send_trees * sizeof (p4est_locidx_t)
+        + quad_plus_data_size * num_send_to[to_proc];
+
+      send_buf[to_proc] = P4EST_ALLOC (char, send_size);
+
+      num_per_tree_send_buf = (p4est_locidx_t *) send_buf[to_proc];
+      memset (num_per_tree_send_buf, 0,
+              num_send_trees * sizeof (p4est_locidx_t));
+      quad_send_buf = (p4est_quadrant_t *) (send_buf[to_proc]
+                                            +
+                                            num_send_trees *
+                                            sizeof (p4est_locidx_t));
+      user_data_send_buf =
+        send_buf[to_proc] + num_send_trees * sizeof (p4est_locidx_t)
+        + num_send_to[to_proc] * sizeof (p4est_quadrant_t);
+
+      /* Pack in the data to be sent */
+
+      my_base = (rank == 0) ? 0 : (global_last_quad_index[rank - 1] + 1);
+      my_begin = begin_send_to[to_proc] - my_base;
+      my_end = begin_send_to[to_proc] + num_send_to[to_proc] - 1 - my_base;
+
+      for (which_tree = first_local_tree; which_tree <= last_local_tree;
+           ++which_tree) {
+        tree = p4est_tree_array_index (trees, which_tree);
+
+        from_begin = (which_tree == first_local_tree) ? 0 :
+          (local_tree_last_quad_index[which_tree - 1] + 1);
+        from_end = local_tree_last_quad_index[which_tree];
+
+        if (from_begin <= my_end && from_end >= my_begin) {
+          /* Need to copy from tree which_tree */
+          tree_from_begin = SC_MAX (my_begin, from_begin) - from_begin;
+          tree_from_end = SC_MIN (my_end, from_end) - from_begin;
+          num_copy = tree_from_end - tree_from_begin + 1;
+
+          num_per_tree_send_buf[which_tree - first_local_tree] = num_copy;
+
+          /* copy quads to send buf */
+          memcpy (quad_send_buf, tree->quadrants.array +
+                  tree_from_begin * sizeof (p4est_quadrant_t),
+                  num_copy * sizeof (p4est_quadrant_t));
+
+          /* set tree in send buf and copy user data */
+          P4EST_LDEBUGF ("partition send %lld [%lld,%lld] quadrants"
+                         " from tree %lld to proc %d\n",
+                         (long long) num_copy, (long long) tree_from_begin,
+                         (long long) tree_from_end, (long long) which_tree,
+                         to_proc);
+          for (il = 0; il < num_copy; ++il) {
+            memcpy (user_data_send_buf + il * data_size,
+                    quad_send_buf[il].p.user_data, data_size);
+            if (data_size) {
+              quad_send_buf[il].p.user_data = NULL;
+            }
+          }
+
+          /* move pointer to beginning of quads that need to be copied */
+          my_begin += num_copy;
+          quad_send_buf += num_copy;
+          user_data_send_buf += num_copy * data_size;
+        }
+      }
+
+      /* Post send operation for the quadrants and their data */
+#ifdef P4EST_ENABLE_MPI
+      P4EST_LDEBUGF ("partition send %lld quadrants to %d\n",
+                     (long long) num_send_to[to_proc], to_proc);
+      mpiret = MPI_Isend (send_buf[to_proc], (int) send_size, MPI_BYTE,
+                          to_proc, P4EST_COMM_PARTITION_GIVEN,
+                          comm, send_request + sk);
+      SC_CHECK_MPI (mpiret);
+      ++sk;
+#endif
+    }
+    else {
+      send_buf[to_proc] = NULL;
+    }
+  }
+#ifdef P4EST_ENABLE_MPI
+  for (; sk < num_proc_send_to; ++sk) {
+    send_request[sk] = MPI_REQUEST_NULL;
+  }
+
+  /* Fill in forest */
+  mpiret =
+    MPI_Waitall (num_proc_recv_from, recv_request, MPI_STATUSES_IGNORE);
+  SC_CHECK_MPI (mpiret);
+#endif
+
+  /* Loop through and fill in */
+
+  /* Calculate the local index of the end of each tree in the repartition */
+  new_local_tree_elem_count =
+    P4EST_ALLOC_ZERO (p4est_locidx_t, trees->elem_count);
+  new_local_tree_elem_count_before =
+    P4EST_ALLOC_ZERO (p4est_locidx_t, trees->elem_count);
+  new_first_local_tree = (p4est_topidx_t) P4EST_TOPIDX_MAX;
+  new_last_local_tree = 0;
+
+  for (from_proc = 0; from_proc < num_procs; ++from_proc) {
+    if (num_recv_from[from_proc] > 0) {
+      first_from_tree = p4est->global_first_position[from_proc].p.which_tree;
+      last_from_tree =
+        p4est->global_first_position[from_proc + 1].p.which_tree;
+      num_recv_trees =          /* same type */
+        last_from_tree - first_from_tree + 1;
+
+      P4EST_LDEBUGF
+        ("partition from %d with trees [%lld,%lld] get %lld trees\n",
+         from_proc, (long long) first_from_tree, (long long) last_from_tree,
+         (long long) num_recv_trees);
+      num_per_tree_recv_buf = (from_proc == rank) ?
+        num_per_tree_local : (p4est_locidx_t *) recv_buf[from_proc];
+
+      for (it = 0; it < num_recv_trees; ++it) {
+
+        if (num_per_tree_recv_buf[it] > 0) {
+          from_tree = first_from_tree + it;     /* same type */
+
+          P4EST_ASSERT (from_tree >= 0
+                        && from_tree < (p4est_topidx_t) trees->elem_count);
+          P4EST_LDEBUGF ("partition recv %lld [%lld,%lld] quadrants"
+                         " from tree %lld from proc %d\n",
+                         (long long) num_per_tree_recv_buf[it],
+                         (long long) new_local_tree_elem_count[from_tree],
+                         (long long) new_local_tree_elem_count[from_tree]
+                         + num_per_tree_recv_buf[it], (long long) from_tree,
+                         from_proc);
+          new_first_local_tree =        /* same type */
+            SC_MIN (new_first_local_tree, from_tree);
+          new_last_local_tree = /* same type */
+            SC_MAX (new_last_local_tree, from_tree);
+          new_local_tree_elem_count[from_tree] +=       /* same type */
+            num_per_tree_recv_buf[it];
+          new_local_tree_elem_count_before[from_tree] +=        /* same type */
+            (from_proc < rank) ? num_per_tree_recv_buf[it] : 0;
+        }
+      }
+    }
+  }
+  if (new_first_local_tree > new_last_local_tree) {
+    new_first_local_tree = -1;
+    new_last_local_tree = -2;
+  }
+  P4EST_VERBOSEF ("partition new forest [%lld,%lld]\n",
+                  (long long) new_first_local_tree,
+                  (long long) new_last_local_tree);
+
+  /* Copy the local quadrants */
+  if (first_local_tree >= 0 && new_first_local_tree >= 0) {
+    P4EST_ASSERT (last_local_tree >= 0 && new_last_local_tree >= 0);
+    first_tree =                /* same type */
+      SC_MIN (first_local_tree, new_first_local_tree);
+  }
+  else {
+    P4EST_ASSERT (last_local_tree == -2 || new_last_local_tree == -2);
+    first_tree =                /* same type */
+      SC_MAX (first_local_tree, new_first_local_tree);
+  }
+  last_tree =                   /* same type */
+    SC_MAX (last_local_tree, new_last_local_tree);
+  my_base = (rank == 0) ? 0 : (global_last_quad_index[rank - 1] + 1);
+  my_begin = begin_send_to[rank] - my_base;
+  my_end = begin_send_to[rank] + num_send_to[rank] - 1 - my_base;
+
+  for (which_tree = first_tree; which_tree <= last_tree; ++which_tree) {
+    tree = p4est_tree_array_index (trees, which_tree);
+    quadrants = &tree->quadrants;
+
+    if (new_local_tree_elem_count[which_tree] > 0) {
+      if (which_tree >= first_local_tree && which_tree <= last_local_tree) {
+
+        num_quadrants = new_local_tree_elem_count[which_tree];
+
+        from_begin = (which_tree == first_local_tree) ? 0 :
+          (local_tree_last_quad_index[which_tree - 1] + 1);
+        from_end = local_tree_last_quad_index[which_tree];
+
+        if (from_begin <= my_end && from_end >= my_begin) {
+          /* Need to keep part of tree which_tree */
+          tree_from_begin = SC_MAX (my_begin, from_begin) - from_begin;
+          tree_from_end = SC_MIN (my_end, from_end) - from_begin;
+          num_copy = tree_from_end - tree_from_begin + 1;
+        }
+        else {
+          tree_from_begin = 0;
+          tree_from_end = -1;
+          num_copy = 0;
+        }
+
+        /* Free all userdata that no longer belongs to this process */
+        zoffset = SC_MIN ((size_t) tree_from_begin, quadrants->elem_count);
+        for (zz = 0; zz < zoffset; ++zz) {
+          quad = p4est_quadrant_array_index (quadrants, zz);
+          p4est_quadrant_free_data (p4est, quad);
+        }
+        zoffset = (size_t) tree_from_end + 1;
+        for (zz = zoffset; zz < quadrants->elem_count; ++zz) {
+          quad = p4est_quadrant_array_index (quadrants, zz);
+          p4est_quadrant_free_data (p4est, quad);
+        }
+
+        if (num_quadrants > (p4est_locidx_t) quadrants->elem_count) {
+          sc_array_resize (quadrants, num_quadrants);
+        }
+
+        P4EST_LDEBUGF ("copying %lld local quads to tree %lld\n",
+                       (long long) num_copy, (long long) which_tree);
+        P4EST_LDEBUGF
+          ("   with %lld(%llu) quads from [%lld, %lld] to [%lld, %lld]\n",
+           (long long) num_quadrants,
+           (unsigned long long) quadrants->elem_count,
+           (long long) tree_from_begin, (long long) tree_from_end,
+           (long long) new_local_tree_elem_count_before[which_tree],
+           (long long) new_local_tree_elem_count_before[which_tree] +
+           num_copy - 1);
+        memmove (quadrants->array +
+                 new_local_tree_elem_count_before[which_tree] *
+                 sizeof (p4est_quadrant_t),
+                 quadrants->array +
+                 tree_from_begin * sizeof (p4est_quadrant_t),
+                 num_copy * sizeof (p4est_quadrant_t));
+
+        if (num_quadrants < (p4est_locidx_t) quadrants->elem_count) {
+          sc_array_resize (quadrants, num_quadrants);
+        }
+      }
+    }
+    else {
+      /*
+       * Check to see if we need to drop a tree because we no longer have
+       * any quadrants in it.
+       */
+      if (which_tree >= first_local_tree && which_tree <= last_local_tree) {
+        /* Free all userdata that no longer belongs to this process */
+        for (zz = 0; zz < quadrants->elem_count; ++zz) {
+          quad = p4est_quadrant_array_index (quadrants, zz);
+          p4est_quadrant_free_data (p4est, quad);
+        }
+
+        /* The whole tree is dropped */
+        P4EST_QUADRANT_INIT (&tree->first_desc);
+        P4EST_QUADRANT_INIT (&tree->last_desc);
+        sc_array_reset (quadrants);
+        tree->quadrants_offset = 0;
+        for (i = 0; i <= P4EST_QMAXLEVEL; ++i) {
+          tree->quadrants_per_level[i] = 0;
+        }
+        tree->maxlevel = 0;
+      }
+    }
+  }
+
+  /* Copy in received quadrants */
+
+  memset (new_local_tree_elem_count_before, 0,
+          trees->elem_count * sizeof (p4est_locidx_t));
+  for (from_proc = 0; from_proc < num_procs; ++from_proc) {
+    if (num_recv_from[from_proc] > 0) {
+      first_from_tree = p4est->global_first_position[from_proc].p.which_tree;
+      last_from_tree =
+        p4est->global_first_position[from_proc + 1].p.which_tree;
+      num_recv_trees =          /* same type */
+        last_from_tree - first_from_tree + 1;
+
+      P4EST_LDEBUGF
+        ("partition copy from %d with trees [%lld,%lld] get %lld trees\n",
+         from_proc, (long long) first_from_tree,
+         (long long) last_from_tree, (long long) num_recv_trees);
+      num_per_tree_recv_buf =
+        (from_proc == rank) ? num_per_tree_local :
+        (p4est_locidx_t *) recv_buf[from_proc];
+
+      quad_recv_buf = (p4est_quadrant_t *) (recv_buf[from_proc]
+                                            + num_recv_trees *
+                                            sizeof (p4est_locidx_t));
+      user_data_recv_buf =
+        recv_buf[from_proc] + num_recv_trees * sizeof (p4est_locidx_t)
+        + num_recv_from[from_proc] * sizeof (p4est_quadrant_t);
+
+      for (it = 0; it < num_recv_trees; ++it) {
+        from_tree = first_from_tree + it;       /* same type */
+        num_copy = num_per_tree_recv_buf[it];
+
+        /* We might have sent trees that are not actual trees.  In
+         * this case the num_copy should be zero
+         */
+        P4EST_ASSERT (num_copy == 0
+                      || (num_copy > 0 && from_tree >= 0
+                          && from_tree < (p4est_topidx_t) trees->elem_count));
+
+        if (num_copy > 0 && rank != from_proc) {
+          tree = p4est_tree_array_index (trees, from_tree);
+          quadrants = &tree->quadrants;
+          num_quadrants = new_local_tree_elem_count[from_tree];
+          sc_array_resize (quadrants, num_quadrants);
+
+          /* copy quadrants */
+          P4EST_LDEBUGF ("copying %lld remote quads to tree %lld"
+                         " with %lld quads from proc %d\n",
+                         (long long) num_copy, (long long) from_tree,
+                         (long long) num_quadrants, from_proc);
+          memcpy (quadrants->array +
+                  new_local_tree_elem_count_before[from_tree]
+                  * sizeof (p4est_quadrant_t), quad_recv_buf,
+                  num_copy * sizeof (p4est_quadrant_t));
+
+          /* copy user data */
+          zoffset = (size_t) new_local_tree_elem_count_before[from_tree];
+          for (zz = 0; zz < (size_t) num_copy; ++zz) {
+            quad = p4est_quadrant_array_index (quadrants, zz + zoffset);
+
+            if (data_size > 0) {
+              quad->p.user_data = sc_mempool_alloc (p4est->user_data_pool);
+              memcpy (quad->p.user_data, user_data_recv_buf + zz * data_size,
+                      data_size);
+            }
+          }
+        }
+
+        if (num_copy > 0) {
+          P4EST_ASSERT (from_tree >= 0
+                        && from_tree < (p4est_topidx_t) trees->elem_count);
+          new_local_tree_elem_count_before[from_tree] +=        /* same type */
+            num_copy;
+        }
+
+        /* increment the recv quadrant pointers */
+        quad_recv_buf += num_copy;
+        user_data_recv_buf += num_copy * data_size;
+      }
+      if (recv_buf[from_proc] != NULL) {
+        P4EST_FREE (recv_buf[from_proc]);
+        recv_buf[from_proc] = NULL;
+      }
+    }
+  }
+
+  /* Set the global index and count of quadrants instead
+   * of calling p4est_comm_count_quadrants
+   */
+  P4EST_FREE (global_last_quad_index);
+  global_last_quad_index = new_global_last_quad_index;
+  P4EST_ASSERT (p4est->global_num_quadrants ==
+                new_global_last_quad_index[num_procs - 1] + 1);
+  P4EST_ASSERT (p4est->global_first_quadrant[0] == 0);
+  for (i = 0; i < num_procs; ++i) {
+    p4est->global_first_quadrant[i + 1] = global_last_quad_index[i] + 1;
+  }
+  P4EST_FREE (new_global_last_quad_index);
+  global_last_quad_index = new_global_last_quad_index = NULL;
+
+  p4est->first_local_tree = new_first_local_tree;
+  p4est->last_local_tree = new_last_local_tree;
+
+  new_local_num_quadrants = 0;
+  for (which_tree = 0; which_tree < new_first_local_tree; ++which_tree) {
+    tree = p4est_tree_array_index (trees, which_tree);
+    tree->quadrants_offset = 0;
+    P4EST_QUADRANT_INIT (&tree->first_desc);
+    P4EST_QUADRANT_INIT (&tree->last_desc);
+  }
+  for (; which_tree <= new_last_local_tree; ++which_tree) {
+    tree = p4est_tree_array_index (trees, which_tree);
+    tree->quadrants_offset = new_local_num_quadrants;
+    quadrants = &tree->quadrants;
+    P4EST_ASSERT (quadrants->elem_count > 0);
+
+    new_local_num_quadrants +=  /* same type */
+      (p4est_locidx_t) quadrants->elem_count;
+
+    for (i = 0; i <= P4EST_QMAXLEVEL; ++i) {
+      tree->quadrants_per_level[i] = 0;
+    }
+    tree->maxlevel = 0;
+    for (zz = 0; zz < quadrants->elem_count; ++zz) {
+      quad = p4est_quadrant_array_index (quadrants, zz);
+      ++tree->quadrants_per_level[quad->level];
+      tree->maxlevel = (int8_t) SC_MAX (quad->level, tree->maxlevel);
+    }
+
+    quad = p4est_quadrant_array_index (quadrants, 0);
+    p4est_quadrant_first_descendant (quad, &tree->first_desc,
+                                     P4EST_QMAXLEVEL);
+    quad = p4est_quadrant_array_index (quadrants, quadrants->elem_count - 1);
+    p4est_quadrant_last_descendant (quad, &tree->last_desc, P4EST_QMAXLEVEL);
+  }
+  for (; which_tree < p4est->connectivity->num_trees; ++which_tree) {
+    tree = p4est_tree_array_index (trees, which_tree);
+    tree->quadrants_offset = new_local_num_quadrants;
+    P4EST_QUADRANT_INIT (&tree->first_desc);
+    P4EST_QUADRANT_INIT (&tree->last_desc);
+  }
+  p4est->local_num_quadrants = new_local_num_quadrants;
+
+  /* Clean up */
+
+#ifdef P4EST_ENABLE_MPI
+  mpiret = MPI_Waitall (num_proc_send_to, send_request, MPI_STATUSES_IGNORE);
+  SC_CHECK_MPI (mpiret);
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (i = 0; i < num_proc_recv_from; ++i) {
+    P4EST_ASSERT (recv_request[i] == MPI_REQUEST_NULL);
+  }
+  for (i = 0; i < num_proc_send_to; ++i) {
+    P4EST_ASSERT (send_request[i] == MPI_REQUEST_NULL);
+  }
+#endif
+  P4EST_FREE (recv_request);
+  P4EST_FREE (send_request);
+#endif
+
+  for (i = 0; i < num_procs; ++i) {
+    if (recv_buf[i] != NULL)
+      P4EST_FREE (recv_buf[i]);
+    if (send_buf[i] != NULL)
+      P4EST_FREE (send_buf[i]);
+  }
+
+  P4EST_FREE (num_per_tree_local);
+  P4EST_FREE (local_tree_last_quad_index);
+  P4EST_FREE (new_local_tree_elem_count);
+  P4EST_FREE (new_local_tree_elem_count_before);
+  P4EST_FREE (recv_buf);
+  P4EST_FREE (send_buf);
+  P4EST_FREE (num_recv_from);
+  P4EST_FREE (num_send_to);
+  P4EST_FREE (begin_send_to);
+
+  p4est_comm_global_partition (p4est, NULL);
+
+  /* Assert that we have a valid partition */
+  P4EST_ASSERT (crc == p4est_checksum (p4est));
+  P4EST_GLOBAL_INFOF
+    ("Done " P4EST_STRING
+     "_partition_given shipped %lld quadrants %.3g%%\n",
+     (long long) total_quadrants_shipped,
+     total_quadrants_shipped * 100. / p4est->global_num_quadrants);
+
+  return total_quadrants_shipped;
+}
diff --git a/src/p4est_algorithms.h b/src/p4est_algorithms.h
new file mode 100644
index 0000000..aed1401
--- /dev/null
+++ b/src/p4est_algorithms.h
@@ -0,0 +1,303 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_ALGORITHMS_H
+#define P4EST_ALGORITHMS_H
+
+#include <p4est.h>
+#include <p4est_extended.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Alloc and initialize the user data of a valid quadrant.
+ * \param [in]  which_tree 0-based index of this quadrant's tree.
+ * \param [in,out]  quad       The quadrant to be initialized.
+ * \param [in]  init_fn    User-supplied callback function to init data.
+ */
+void                p4est_quadrant_init_data (p4est_t * p4est,
+                                              p4est_topidx_t which_tree,
+                                              p4est_quadrant_t * quad,
+                                              p4est_init_t init_fn);
+
+/** Free the user data of a valid quadrant.
+ * \param [in,out]  quad The quadrant whose data shall be freed.
+ */
+void                p4est_quadrant_free_data (p4est_t * p4est,
+                                              p4est_quadrant_t * quad);
+
+/** Computes a machine-independent checksum of a list of quadrants.
+ * \param [in] quadrants       Array of quadrants.
+ * \param [in,out] checkarray  Temporary array of elem_size 4.
+ *                             Will be resized to quadrants->elem_count * 3.
+ *                             If it is NULL, will be allocated internally.
+ * \param [in] first_quadrant  Index of the quadrant to start with.
+ *                             Can be between 0 and elem_count (inclusive).
+ */
+unsigned            p4est_quadrant_checksum (sc_array_t * quadrants,
+                                             sc_array_t * checkarray,
+                                             size_t first_quadrant);
+
+/** Test if a tree is sorted in Morton ordering.
+ * \return Returns true if sorted, false otherwise.
+ * \note Duplicate quadrants are not allowed.
+ */
+int                 p4est_tree_is_sorted (p4est_tree_t * tree);
+
+/** Test if a tree is sorted in Morton ordering and linear.
+ * \return Returns true if linear, false otherwise.
+ * \note Linear means that the tree has no overlaps.
+ */
+int                 p4est_tree_is_linear (p4est_tree_t * tree);
+
+/** Test if a tree is sorted in Morton ordering and complete.
+ * \return Returns true if complete, false otherwise.
+ * \note Complete means that the tree has no holes and no overlaps.
+ */
+int                 p4est_tree_is_complete (p4est_tree_t * tree);
+
+/** Check if a tree is sorted/linear except for diagonally outside corners.
+ * \param [in]  check_linearity  Boolean for additional check for linearity.
+ * \return Returns true if almost sorted/linear, false otherwise.
+ */
+int                 p4est_tree_is_almost_sorted (p4est_tree_t * tree,
+                                                 int check_linearity);
+
+/** Print the quadrants in a tree.
+ * Prints one line per quadrant with x, y, level and a string.
+ * The string denotes the relation to the previous quadrant and can be:
+ *   Fn  for the first quadrant in the tree with child id n
+ *   I   for identical quadrants
+ *   R   for a quadrant that with smaller Morton index
+ *   Cn  for child id n
+ *   Sn  for sibling with child id n
+ *   D   for a descendant
+ *   Nn   for a next quadrant in the tree with no holes in between and child id n
+ *   qn  for a general quadrant whose child id is n
+ * \param [in] tree        Any (possibly incomplete, unsorted) tree to be printed.
+ */
+void                p4est_tree_print (int log_priority, p4est_tree_t * tree);
+
+/** Locally check forest/connectivity structures for equality.
+ * \param [in] p4est1    The first forest to be compared.
+ * \param [in] p4est2    The second forest to be compared.
+ * \param [in] compare_data     Also check if quadrant data are identical.
+ * \return          Returns true if forests and their connectivities are equal.
+ */
+int                 p4est_is_equal (p4est_t * p4est1, p4est_t * p4est2,
+                                    int compare_data);
+
+/** Check a forest for validity and allreduce the result.
+ * Some properties of a valid forest are:
+ *    the quadrant counters are consistent
+ *    all trees are complete
+ *    all non-local trees are empty
+ * \param [in] p4est    The forest to be tested.
+ * \return              Returns true if valid, false otherwise.
+ */
+int                 p4est_is_valid (p4est_t * p4est);
+
+/** Compute the overlap of a number of insulation layers with a tree.
+ * Every quadrant out of the insulation layer of the quadrants in \a in
+ * except the quadrant itself is checked for overlap of quadrants
+ * from all trees that are smaller by at least two levels and thus
+ * can cause a split. Those elements that cause a split (as determined by the
+ * p4est_balance_*_test routines) create quadrants in \a out that will
+ * reproduce those splits when \a in is balanced.
+ * Note: Use this version if you are using less than full balance.
+ *
+ * \param [in] p4est    The p4est to work on.
+ * \param [in] in       A piggy-sorted linear list of quadrants.
+ *                      The piggy2->from_tree member must be set.
+ * \param [in,out] out  A piggy-sorted subset of tree->quadrants.
+ * \param [in] balance  The type of balance condition that should be enforced.
+ * \param [in] borders  Array of arrays of tree border elements: if not NULL,
+ *                      this will be used to fill \a out.
+ * \param [in] inseeds  The seeds that \a in generates locally.
+ */
+void                p4est_tree_compute_overlap (p4est_t * p4est,
+                                                sc_array_t * in,
+                                                sc_array_t * out,
+                                                p4est_connect_type_t balance,
+                                                sc_array_t * borders,
+                                                sc_array_t * inseeds);
+
+/** Gets the reduced representation of the overlap that results from using
+ * p4est_tree_compute_overlap_new
+ * \param [in,out] out  A piggy-sorted subset of tree->quadrants.
+  */
+void                p4est_tree_uniqify_overlap (sc_array_t * out);
+
+/** Removes quadrants that are outside the owned tree boundaries from a tree.
+ * \param [in,out] p4est    The p4est to work on.
+ * \param [in] which_tree   Index to a sorted owned tree in the p4est.
+ * \return                  Returns the number of removed quadrants.
+ */
+size_t              p4est_tree_remove_nonowned (p4est_t * p4est,
+                                                p4est_topidx_t which_tree);
+
+/** Constructs a minimal linear octree between two octants.
+ *
+ * This is alogorithm 2 from H. Sundar, R.S. Sampath and G. Biros
+ * with the additional improvements that we do not require sorting
+ * and the runtime is O(N).
+ *
+ * \pre \a q1 < \a q2 in the Morton ordering.
+ *
+ * \param [in]  p4est      Used for the memory pools and quadrant init.
+ * \param [in]  q1         First input quadrant.  Data init'ed if included.
+ * \param [in]  include_q1 Flag to specify whether q1 is included.
+ * \param [in]  q2         Second input quadrant.  Data init'ed if included.
+ * \param [in]  include_q2 Flag to specify whether q2 is included.
+ * \param [out] tree       Initialized tree with zero elements.
+ * \param [in]  which_tree The 0-based index of \a tree which is needed for
+ *                         the \c p4est_quadrant_init_data routine.
+ * \param [in]  init_fn    Callback function to initialize the user_data
+ *                         which is already allocated automatically.
+ */
+void                p4est_complete_region (p4est_t * p4est,
+                                           const p4est_quadrant_t * q1,
+                                           int include_q1,
+                                           const p4est_quadrant_t * q2,
+                                           int include_q2,
+                                           p4est_tree_t * tree,
+                                           p4est_topidx_t which_tree,
+                                           p4est_init_t init_fn);
+
+/** Completes a sorted tree within a p4est. It may have exterior quadrants.
+ * The completed tree will have only owned quadrants and no overlap.
+ * \param [in,out] p4est      The p4est to work on.
+ * \param [in]     which_tree The 0-based index of the subtree to complete.
+ * \param [in]     init_fn    Callback function to initialize the user_data
+ *                            which is already allocated automatically.
+ */
+void                p4est_complete_subtree (p4est_t * p4est,
+                                            p4est_topidx_t which_tree,
+                                            p4est_init_t init_fn);
+
+/** Balances a sorted tree within a p4est. It may have exterior quadrants.
+ * The completed tree will have only owned quadrants and no overlap.
+ * \param [in,out] p4est      The p4est to work on.
+ * \param [in]     btype      The balance type (face or corner).
+ * \param [in]     which_tree The 0-based index of the subtree to balance.
+ * \param [in]     init_fn    Callback function to initialize the user_data
+ *                            which is already allocated automatically.
+ */
+void                p4est_balance_subtree (p4est_t * p4est,
+                                           p4est_connect_type_t btype,
+                                           p4est_topidx_t which_tree,
+                                           p4est_init_t init_fn);
+
+void                p4est_balance_border (p4est_t * p4est,
+                                          p4est_connect_type_t btype,
+                                          p4est_topidx_t which_tree,
+                                          p4est_init_t init_fn,
+                                          p4est_replace_t replace_fn,
+                                          sc_array_t * borders);
+
+/** Remove overlaps from a sorted list of quadrants.
+ *
+ * This is alogorithm 8 from H. Sundar, R.S. Sampath and G. Biros
+ * with the additional improvement that it works in-place.
+ *
+ * \param [in]     p4est used for the memory pool and quadrant free.
+ * \param [in,out] tree   A sorted tree to be linearized in-place.
+ * \return                Returns the number of removed quadrants.
+ */
+size_t              p4est_linearize_tree (p4est_t * p4est,
+                                          p4est_tree_t * tree);
+
+/** Compute correction of partition for a process.
+ *
+ * The correction denotes how many quadrants the process with id \a rank takes
+ * from (if correction positive) or gives to (if correction negative) the
+ * previous process with id \a rank-1 in order to assign a family of quadrants
+ * to one process.
+ * The process with the highest number of quadrants of a family gets all
+ * quadrants belonging to this family from other processes. If this applies to
+ * several processes, then the process with the lowest id gets the quadrants.
+ * A process can give more quadrants than it owns, if it passes quadrants from
+ * other processes.
+ *
+ * \param [in] partition       first global quadrant index for each process (+1)
+ * \param [in] num_procs       number of processes
+ * \param [in] rank            process id for which correction is computed
+ * \param [in] min_quadrant_id minimal global quadrant index of family
+ * \param [in] max_quadrant_id maximal global quadrant index of family
+ * \return                     correction for process \a rank
+ */
+p4est_locidx_t      p4est_partition_correction (p4est_gloidx_t *
+                                                partition,
+                                                int num_procs,
+                                                int rank,
+                                                p4est_gloidx_t
+                                                min_quadrant_id,
+                                                p4est_gloidx_t
+                                                max_quadrant_id);
+
+/** Correct partition counters to allow one level of coarsening.
+ * No quadrants are actually shipped, just the desired number is updated.
+ * This function guarantees that empty processors remain empty.
+ * This function is collective and acts as a synchronization point.
+ *
+ * \param [in] p4est                     forest whose partition is corrected
+ * \param [in,out] num_quadrants_in_proc partition that will be corrected
+ * \return                               total absolute number of moved
+ *                                       quadrants.  In practice, at most
+ *                                       a small number per processor.
+ */
+p4est_gloidx_t      p4est_partition_for_coarsening (p4est_t * p4est,
+                                                    p4est_locidx_t *
+                                                    num_quadrants_in_proc);
+
+/** Find next non-empty process.
+ *
+ * Finds the next process id >= \a rank which is not empty according to
+ * \a num_quadrants_in_proc.
+ *
+ * \param [in] rank                  process id where search starts
+ * \param [in] num_proc              number of processes
+ * \param [in] num_quadrants_in_proc number of quadrants for each process
+ * \return                           process id of a non empty process
+ */
+int                 p4est_next_nonempty_process (int rank,
+                                                 int num_procs,
+                                                 p4est_locidx_t *
+                                                 num_quadrants_in_proc);
+
+/** Partition \a p4est given the number of quadrants per proc.
+ *
+ * Given the desired number of quadrants per proc \a num_quadrants_in_proc
+ * the forest \a p4est is partitioned.
+ *
+ * \param [in,out] p4est the forest that is partitioned.
+ * \param [in]     num_quadrants_in_proc  an integer array of the number of
+ *                                        quadrants desired per processor.
+ * \return  Returns the global count of shipped quadrants.
+ */
+p4est_gloidx_t      p4est_partition_given (p4est_t * p4est,
+                                           const p4est_locidx_t *
+                                           num_quadrants_in_proc);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_ALGORITHMS_H */
diff --git a/src/p4est_balance.c b/src/p4est_balance.c
new file mode 100644
index 0000000..9ecd50c
--- /dev/null
+++ b/src/p4est_balance.c
@@ -0,0 +1,984 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2011 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef P4_TO_P8
+#include <p8est_bits.h>
+#include <p8est_communication.h>
+#include <p8est_search.h>
+#else
+#include <p4est_bits.h>
+#include <p4est_communication.h>
+#include <p4est_search.h>
+#endif /* !P4_TO_P8 */
+
+/* We have a location, and a \a level quadrant that must be shifted by
+ * \a distance (>= 0) to be at the location.  This returns the largest quadrant
+ * that can exist at \a location and be in balance with the \a level quadrant.
+ */
+static inline int
+p4est_balance_kernel_1d (p4est_qcoord_t distance, int level)
+{
+  int                 shift = P4EST_MAXLEVEL - level;
+  P4EST_ASSERT (0 <= level && level <= P4EST_QMAXLEVEL);
+  /* the distance only makes sense if it is an integer number of \a level
+   * distances */
+  P4EST_ASSERT (distance >= 0);
+  P4EST_ASSERT (!(distance & (~(((p4est_qcoord_t) - 1) << shift))));
+  distance >>= shift;
+  /* The theory says we should use ((distance + 1)&(~1) + 1), but
+   * using distance + 1 is equivalent for all distance >= 0 */
+  distance++;
+
+  return SC_MAX (0, level - SC_LOG2_32 (distance));
+}
+
+/* This is the kernel for 2D balance with face-only balancing */
+static inline int
+p4est_balance_kernel_2d (p4est_qcoord_t dx, p4est_qcoord_t dy, int level)
+{
+  int                 shift = P4EST_MAXLEVEL - level;
+  p4est_qcoord_t      distance;
+
+  P4EST_ASSERT (0 <= level && level <= P4EST_QMAXLEVEL);
+  /* the distance only makes sense if it is an integer number of \a level
+   * distances */
+  P4EST_ASSERT (dx >= 0);
+  P4EST_ASSERT (!(dx & (~(((p4est_qcoord_t) - 1) << shift))));
+  P4EST_ASSERT (dy >= 0);
+  P4EST_ASSERT (!(dy & (~(((p4est_qcoord_t) - 1) << shift))));
+
+  dx >>= shift;
+  /* get the smallest even number greater than or equal to dx */
+  dx = (dx + 1) & (~((p4est_qcoord_t) 0x1));
+
+  dy >>= shift;
+  /* get the smallest even number greater than or equal to dy */
+  dy = (dy + 1) & (~((p4est_qcoord_t) 0x1));
+
+  /* the + 1 guarantees the correct answer even for (0, 0) */
+  distance = dx + dy + 1;
+
+  return SC_MAX (0, level - SC_LOG2_32 (distance));
+}
+
+#ifdef P4_TO_P8
+/* This is the kernel for 3d balance with face and edge balancing */
+static inline int
+p8est_balance_kernel_3d_edge (p4est_qcoord_t dx, p4est_qcoord_t dy,
+                              p4est_qcoord_t dz, int level)
+{
+  int                 shift = P4EST_MAXLEVEL - level;
+  int                 xbit, ybit, zbit;
+  int                 maxbit;
+  p4est_qcoord_t      bitwor;
+  int                 ret;
+
+  P4EST_ASSERT (dx >= 0);
+  P4EST_ASSERT (!(dx & (~(((p4est_qcoord_t) - 1) << shift))));
+  P4EST_ASSERT (dy >= 0);
+  P4EST_ASSERT (!(dy & (~(((p4est_qcoord_t) - 1) << shift))));
+  P4EST_ASSERT (dz >= 0);
+  P4EST_ASSERT (!(dz & (~(((p4est_qcoord_t) - 1) << shift))));
+
+  if (!dx && !dy && !dz) {
+    return level;
+  }
+
+  dx >>= shift;
+  /* get the smallest even number greater than or equal to dx */
+  dx = (dx + 1) & (~((p4est_qcoord_t) 0x1));
+
+  dy >>= shift;
+  /* get the smallest even number greater than or equal to dy */
+  dy = (dy + 1) & (~((p4est_qcoord_t) 0x1));
+
+  dz >>= shift;
+  /* get the smallest even number greater than or equal to dz */
+  dz = (dz + 1) & (~((p4est_qcoord_t) 0x1));
+
+  xbit = SC_LOG2_32 (dx);
+  maxbit = xbit;
+  ybit = SC_LOG2_32 (dy);
+  maxbit = SC_MAX (maxbit, ybit);
+  zbit = SC_LOG2_32 (dz);
+  maxbit = SC_MAX (maxbit, zbit);
+  P4EST_ASSERT (maxbit >= 1);
+
+  /* we want to carry a 1 when there are three 1 bits, so we find places
+   * where there is at least one 1 bit and subtract one 1 bit: then if we
+   * sum, the binary carry rule will give the correct result */
+  bitwor = (dx | dy | dz);
+  ret = SC_LOG2_32 (dx + dy + dz - bitwor);
+  /* we have to guard against the case when the leading position has one 1
+   * bit. */
+  ret = SC_MAX (maxbit, ret);
+  return SC_MAX (0, level - ret);
+}
+
+/* This is the kernel for 3d balance with face balancing only */
+static inline int
+p8est_balance_kernel_3d_face (p4est_qcoord_t dx, p4est_qcoord_t dy,
+                              p4est_qcoord_t dz, int level)
+{
+  int                 shift = P4EST_MAXLEVEL - level;
+  int                 maxbit;
+  int                 yzbit, zxbit, xybit;
+  p4est_qcoord_t      dyz, dzx, dxy, bitwor;
+  int                 ret;
+
+  P4EST_ASSERT (dx >= 0);
+  P4EST_ASSERT (!(dx & (~(((p4est_qcoord_t) - 1) << shift))));
+  P4EST_ASSERT (dy >= 0);
+  P4EST_ASSERT (!(dy & (~(((p4est_qcoord_t) - 1) << shift))));
+  P4EST_ASSERT (dz >= 0);
+  P4EST_ASSERT (!(dz & (~(((p4est_qcoord_t) - 1) << shift))));
+
+  if (!dx && !dy && !dz) {
+    return level;
+  }
+
+  dx >>= shift;
+  /* get the smallest even number greater than or equal to dx */
+  dx = (dx + 1) & (~((p4est_qcoord_t) 0x1));
+
+  dy >>= shift;
+  /* get the smallest even number greater than or equal to dy */
+  dy = (dy + 1) & (~((p4est_qcoord_t) 0x1));
+
+  dz >>= shift;
+  /* get the smallest even number greater than or equal to dz */
+  dz = (dz + 1) & (~((p4est_qcoord_t) 0x1));
+
+  /* this problem is dual to dual to kernel 3d edge */
+  dyz = dy + dz;
+  dzx = dz + dx;
+  dxy = dx + dy;
+
+  yzbit = SC_LOG2_32 (dyz);
+  maxbit = yzbit;
+  zxbit = SC_LOG2_32 (dzx);
+  maxbit = SC_MAX (maxbit, zxbit);
+  xybit = SC_LOG2_32 (dxy);
+  maxbit = SC_MAX (maxbit, xybit);
+  P4EST_ASSERT (maxbit >= 1);
+
+  /* we want to carry a 1 when there are three 1 bits, so we find places
+   * where there is at least one 1 bit and subtract one 1 bit: then if we
+   * sum, the binary carry rule will give the correct result */
+  bitwor = (dyz | dzx | dxy);
+  ret = SC_LOG2_32 (dyz + dzx + dxy - bitwor);
+  /* we have to guard against the case when the leading position has one 1
+   * bit. */
+  ret = SC_MAX (maxbit, ret);
+  return SC_MAX (0, level - ret);
+}
+#endif
+
+static void         p4est_bal_corner_con_internal (p4est_quadrant_t const *q,
+                                                   p4est_quadrant_t * p,
+                                                   int corner,
+                                                   int balance,
+                                                   int *consisent);
+
+static void         p4est_bal_face_con_internal (p4est_quadrant_t const *q,
+                                                 p4est_quadrant_t * p,
+                                                 int face, int balance,
+                                                 int *consisent,
+                                                 p4est_quadrant_t * add);
+
+#ifdef P4_TO_P8
+static void         p8est_bal_edge_con_internal (p4est_quadrant_t const *q,
+                                                 p4est_quadrant_t * p,
+                                                 int edge, int balance,
+                                                 int *consistent,
+                                                 p4est_quadrant_t * add);
+#endif
+
+/* \a corner is the corner of \a p closest to \a q: the corner of \a q closest
+ * to \a p is the dual of \a corner */
+static void
+p4est_bal_corner_con_internal (p4est_quadrant_t const *q,
+                               p4est_quadrant_t * p,
+                               int corner, int balance, int *consistent)
+{
+  int                 qlevel = q->level;
+  int                 plevel = p->level;
+  int                 blevel;
+  p4est_qcoord_t      qlen, plen, mask;
+  p4est_qcoord_t      dx, dy, dist;
+#ifdef P4_TO_P8
+  p4est_qcoord_t      dz;
+#endif
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (p4est_quadrant_is_extended (p));
+
+  if (qlevel <= plevel) {
+    if (consistent != NULL) {
+      *consistent = 1;
+    }
+    return;
+  }
+
+  qlen = P4EST_QUADRANT_LEN (qlevel);
+  plen = P4EST_QUADRANT_LEN (plevel);
+
+  dx = (corner & 1) ? ((q->x + qlen) - (p->x + plen)) : p->x - q->x;
+  P4EST_ASSERT (dx >= 0);
+  dy = (corner & 2) ? ((q->y + qlen) - (p->y + plen)) : p->y - q->y;
+  P4EST_ASSERT (dy >= 0);
+#ifdef P4_TO_P8
+  dz = (corner & 4) ? ((q->z + qlen) - (p->z + plen)) : p->z - q->z;
+  P4EST_ASSERT (dz >= 0);
+#endif
+
+#ifndef P4_TO_P8
+  if (balance) {
+    dist = SC_MAX (dx, dy);
+    blevel = p4est_balance_kernel_1d (dist, qlevel);
+  }
+  else {
+    blevel = p4est_balance_kernel_2d (dx, dy, qlevel);
+  }
+#else
+  switch (balance) {
+  case 0:
+    blevel = p8est_balance_kernel_3d_face (dx, dy, dz, qlevel);
+    break;
+  case 1:
+    blevel = p8est_balance_kernel_3d_edge (dx, dy, dz, qlevel);
+    break;
+  case 2:
+    dist = SC_MAX (dx, dy);
+    dist = SC_MAX (dist, dz);
+    blevel = p4est_balance_kernel_1d (dist, qlevel);
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+#endif
+
+  if (blevel <= plevel) {
+    if (consistent != NULL) {
+      *consistent = 1;
+    }
+    return;
+  }
+
+  if (consistent != NULL) {
+    *consistent = 0;
+  }
+
+  mask = -1 << (P4EST_MAXLEVEL - blevel);
+  p->x = q->x + ((corner & 1) ? -dx : dx);
+  p->x &= mask;
+  p->y = q->y + ((corner & 2) ? -dy : dy);
+  p->y &= mask;
+#ifdef P4_TO_P8
+  p->z = q->z + ((corner & 4) ? -dz : dz);
+  p->z &= mask;
+#endif
+  p->level = blevel;
+  P4EST_ASSERT (p4est_quadrant_is_extended (p));
+}
+
+static void
+p4est_bal_face_con_internal (p4est_quadrant_t const *q,
+                             p4est_quadrant_t * p, int face,
+                             int balance, int *consistent,
+                             p4est_quadrant_t * add)
+{
+  int                 qlevel = q->level;
+  int                 plevel = p->level;
+  int                 blevel;
+
+  int                 child;
+#ifdef P4_TO_P8
+  int                 edge;
+  int                 dual;
+  int                 achild = -1;
+#endif
+  int                 recon;
+  p4est_quadrant_t    porig;
+  p4est_quadrant_t    temp;
+  p4est_quadrant_t    a;
+  int                 i;
+#ifndef P4_TO_P8
+  int                 nconextra = 3;
+#else
+  int                 nconextra = 9;
+  int                 j;
+  p4est_qcoord_t      b2mask;
+#endif
+  p4est_qcoord_t      distance;
+  p4est_qcoord_t      qlen, plen, mask, pmask;
+  p4est_qcoord_t      b1len;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (p4est_quadrant_is_extended (p));
+
+  if (qlevel <= plevel) {
+    if (consistent != NULL) {
+      *consistent = 1;
+    }
+    return;
+  }
+
+  qlen = P4EST_QUADRANT_LEN (qlevel);
+  plen = P4EST_QUADRANT_LEN (plevel);
+
+  switch (face) {
+  case 0:
+    distance = p->x - q->x;
+    break;
+  case 1:
+    distance = (q->x + qlen) - (p->x + plen);
+    break;
+  case 2:
+    distance = p->y - q->y;
+    break;
+  case 3:
+    distance = (q->y + qlen) - (p->y + plen);
+    break;
+#ifdef P4_TO_P8
+  case 4:
+    distance = p->z - q->z;
+    break;
+  case 5:
+    distance = (q->z + qlen) - (p->z + plen);
+    break;
+#endif
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+
+  P4EST_ASSERT (distance >= 0);
+
+  blevel = p4est_balance_kernel_1d (distance, q->level);
+
+  if (blevel <= plevel) {
+    if (consistent != NULL) {
+      *consistent = 1;
+    }
+    return;
+  }
+
+  if (consistent != NULL) {
+    *consistent = 0;
+  }
+
+  porig = *p;
+
+  *p = *q;
+
+  /* shift a until it is inside p */
+  switch (face) {
+  case 0:
+    p->x += distance;
+    break;
+  case 1:
+    p->x -= distance;
+    break;
+  case 2:
+    p->y += distance;
+    break;
+  case 3:
+    p->y -= distance;
+    break;
+#ifdef P4_TO_P8
+  case 4:
+    p->z += distance;
+    break;
+  case 5:
+    p->z -= distance;
+    break;
+#endif
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+
+  mask = -1 << (P4EST_MAXLEVEL - blevel);
+  p->x &= mask;
+  p->y &= mask;
+#ifdef P4_TO_P8
+  p->z &= mask;
+#endif
+  p->level = blevel;
+  P4EST_ASSERT (p4est_quadrant_is_extended (p));
+
+  if (add != NULL) {
+
+    add[nconextra / 2] = *p;
+
+    /* this is the only quad needed if it is only one level smaller than the
+     * original quadrant */
+    if (blevel == plevel - 1) {
+      return;
+    }
+
+    mask = -1 << (P4EST_MAXLEVEL - (blevel - 1));
+    pmask = -1 << (P4EST_MAXLEVEL - (plevel));
+    a = *p;
+    a.x &= mask;
+    a.y &= mask;
+#ifdef P4_TO_P8
+    a.z &= mask;
+#endif
+    a.level = blevel - 1;
+
+    b1len = P4EST_QUADRANT_LEN (blevel - 1);
+#ifndef P4_TO_P8
+    for (i = -1; i <= 1; i += 2) {
+      temp = a;
+      /* temp is in a family group one family group over from temp */
+      if (face / 2 == 0) {
+        temp.y += i * b1len;
+      }
+      else {
+        temp.x += i * b1len;
+      }
+
+      if ((temp.x & pmask) != porig.x || (temp.y & pmask) != porig.y) {
+        /* only test other descendants of p */
+        continue;
+      }
+
+      child = p4est_face_corners[face][(1 - i) / 2];
+
+      p4est_bal_corner_con_internal (q, &temp, child, balance, &recon);
+
+      if (!recon) {
+        add[1 + i] = temp;
+      }
+    }
+#else
+    b2mask = -1 << (P4EST_MAXLEVEL - (blevel - 2));
+    if (!balance) {
+      achild = p8est_quadrant_child_id (&a);
+    }
+    for (j = -1; j <= 1; j++) {
+      for (i = -1; i <= 1; i++) {
+        if (!i & !j) {
+          continue;
+        }
+        temp = a;
+        switch (face / 2) {
+        case 0:
+          temp.y += i * b1len;
+          temp.z += j * b1len;
+          break;
+        case 1:
+          temp.x += i * b1len;
+          temp.z += j * b1len;
+          break;
+        case 2:
+          temp.x += i * b1len;
+          temp.y += j * b1len;
+          break;
+        default:
+          SC_ABORT_NOT_REACHED ();
+        }
+
+        if ((temp.x & pmask) != porig.x || (temp.y & pmask) != porig.y ||
+            (temp.z & pmask) != porig.z) {
+          /* only test other descendants of p */
+          continue;
+        }
+
+        if (i && j) {
+
+          child = p8est_face_corners[face][(1 - j) + (1 - i) / 2];
+
+          /* for face only balance, we need to check a larger neighbor in one
+           * instance */
+          if (!balance) {
+            dual = p8est_face_corners[face][(1 + j) + (1 + i) / 2];
+
+            if (achild == dual) {
+              temp.x &= b2mask;
+              temp.y &= b2mask;
+              temp.z &= b2mask;
+              temp.level = blevel - 2;
+            }
+          }
+
+          p4est_bal_corner_con_internal (q, &temp, child, balance, &recon);
+
+          if (!recon) {
+            add[4 + 3 * j + i] = temp;
+          }
+        }
+        else {
+          if (!i) {
+            edge = p8est_face_edges[face][(1 - j) / 2];
+          }
+          else {
+            edge = p8est_face_edges[face][2 + (1 - i) / 2];
+          }
+
+          p8est_bal_edge_con_internal (q, &temp, edge, balance, &recon, NULL);
+
+          if (!recon) {
+            add[4 + 3 * j + i] = temp;
+          }
+        }
+
+      }
+    }
+
+    if (!balance) {
+      for (j = -1; j <= 1; j += 2) {
+        for (i = -1; i <= 1; i += 2) {
+          if (add[4 + 3 * j + i].level != -1 &&
+              add[4 + 3 * j + i].level < blevel) {
+            if (add[4 + 3 * j].level != -1 || add[4 + i].level != -1) {
+              memset (&(add[4 + 3 * j + i]), -1, sizeof (p4est_quadrant_t));
+            }
+          }
+        }
+      }
+    }
+#endif
+  }
+}
+
+#ifdef P4_TO_P8
+static void
+p8est_bal_edge_con_internal (p4est_quadrant_t const *q,
+                             p4est_quadrant_t * p, int edge,
+                             int balance, int *consistent,
+                             p4est_quadrant_t * add)
+{
+  int                 plevel = p->level;
+  int                 qlevel = q->level;
+  int                 blevel;
+  int                 child;
+  int                 recon;
+  p4est_quadrant_t    porig;
+  p4est_quadrant_t    temp;
+  p4est_quadrant_t    a;
+  p4est_qcoord_t      dx, dy;
+  p4est_qcoord_t      dist;
+  p4est_qcoord_t      qlen, plen, mask;
+  p4est_qcoord_t      b1len, pmask;
+  int                 i;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (p4est_quadrant_is_extended (p));
+
+  if (qlevel <= plevel) {
+    if (consistent != NULL) {
+      *consistent = 1;
+    }
+    return;
+  }
+
+  qlen = P4EST_QUADRANT_LEN (qlevel);
+  plen = P4EST_QUADRANT_LEN (plevel);
+
+  switch (edge / 4) {
+  case 0:
+    dx = (edge & 1) ? (q->y + qlen) - (p->y + plen) : p->y - q->y;
+    dy = (edge & 2) ? (q->z + qlen) - (p->z + plen) : p->z - q->z;
+    break;
+  case 1:
+    dx = (edge & 1) ? (q->x + qlen) - (p->x + plen) : p->x - q->x;
+    dy = (edge & 2) ? (q->z + qlen) - (p->z + plen) : p->z - q->z;
+    break;
+  case 2:
+    dx = (edge & 1) ? (q->x + qlen) - (p->x + plen) : p->x - q->x;
+    dy = (edge & 2) ? (q->y + qlen) - (p->y + plen) : p->y - q->y;
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+  P4EST_ASSERT (dx >= 0);
+  P4EST_ASSERT (dy >= 0);
+
+  if (balance) {
+    dist = SC_MAX (dx, dy);
+    blevel = p4est_balance_kernel_1d (dist, qlevel);
+  }
+  else {
+    blevel = p4est_balance_kernel_2d (dx, dy, qlevel);
+  }
+
+  if (blevel <= plevel) {
+    if (consistent != NULL) {
+      *consistent = 1;
+    }
+    return;
+  }
+
+  if (consistent != NULL) {
+    *consistent = 0;
+  }
+
+  porig = *p;
+  *p = *q;
+
+  switch (edge / 4) {
+  case 0:
+    p->y += (edge & 1) ? -dx : dx;
+    p->z += (edge & 2) ? -dy : dy;
+    break;
+  case 1:
+    p->x += (edge & 1) ? -dx : dx;
+    p->z += (edge & 2) ? -dy : dy;
+    break;
+  case 2:
+    p->x += (edge & 1) ? -dx : dx;
+    p->y += (edge & 2) ? -dy : dy;
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+  mask = -1 << (P4EST_MAXLEVEL - blevel);
+  p->x &= mask;
+  p->y &= mask;
+  p->z &= mask;
+  p->level = blevel;
+  P4EST_ASSERT (p4est_quadrant_is_extended (p));
+
+  if (add != NULL) {
+    add[1] = *p;
+
+    /* this is the only quad needed if it is only one level smaller than the
+     * original quadrant */
+    if (blevel == plevel - 1) {
+      return;
+    }
+
+    mask = -1 << (P4EST_MAXLEVEL - (blevel - 1));
+    pmask = -1 << (P4EST_MAXLEVEL - (plevel));
+    a = *p;
+    a.x &= mask;
+    a.y &= mask;
+    a.z &= mask;
+    a.level = blevel - 1;
+
+    b1len = P4EST_QUADRANT_LEN (blevel - 1);
+    for (i = -1; i <= 1; i += 2) {
+      temp = a;
+      /* temp is in a family group one family group over from temp */
+      switch (edge / 4) {
+      case 0:
+        temp.x += i * b1len;
+        break;
+      case 1:
+        temp.y += i * b1len;
+        break;
+      case 2:
+        temp.z += i * b1len;
+        break;
+      default:
+        SC_ABORT_NOT_REACHED ();
+      }
+
+      if ((temp.x & pmask) != porig.x || (temp.y & pmask) != porig.y ||
+          (temp.z & pmask) != porig.z) {
+        /* only test other descendants of p */
+        continue;
+      }
+
+      child = p8est_edge_corners[edge][(1 - i) / 2];
+
+      p4est_bal_corner_con_internal (q, &temp, child, balance, &recon);
+
+      if (!recon) {
+        add[1 + i] = temp;
+      }
+    }
+  }
+}
+#endif
+
+int
+p4est_balance_seeds_face (p4est_quadrant_t * q,
+                          p4est_quadrant_t * p,
+                          int face, p4est_connect_type_t balance,
+                          sc_array_t * seeds)
+{
+  p4est_quadrant_t    temp = *p;
+  p4est_quadrant_t   *s;
+  int                 ibalance;
+  int                 consistent;
+#ifndef P4_TO_P8
+  int                 nextra = 3;
+  p4est_quadrant_t    add[3];
+#else
+  int                 nextra = 9;
+  p8est_quadrant_t    add[9];
+#endif
+  int                 i;
+
+  P4EST_ASSERT (seeds == NULL ||
+                seeds->elem_size == sizeof (p4est_quadrant_t));
+
+  if (balance == P4EST_CONNECT_FULL) {
+    ibalance = P4EST_DIM - 1;
+  }
+#ifdef P4_TO_P8
+  else if (balance == P8EST_CONNECT_EDGE) {
+    ibalance = 1;
+  }
+#endif
+  else {
+    ibalance = 0;
+  }
+
+  if (seeds == NULL) {
+    p4est_bal_face_con_internal (q, &temp, face, ibalance, &consistent, NULL);
+    return !consistent;
+  }
+  else {
+    memset (add, -1, nextra * sizeof (p4est_quadrant_t));
+    p4est_bal_face_con_internal (q, &temp, face, ibalance, &consistent, add);
+
+    sc_array_resize (seeds, 0);
+    if (!consistent) {
+      for (i = 0; i < nextra; i++) {
+        if (add[i].level != -1) {
+          sc_array_resize (seeds, seeds->elem_count + 1);
+          s = p4est_quadrant_array_index (seeds, seeds->elem_count - 1);
+
+          *s = add[i];
+        }
+      }
+    }
+
+    return !consistent;
+  }
+}
+
+int
+p4est_balance_seeds_corner (p4est_quadrant_t * q,
+                            p4est_quadrant_t * p,
+                            int corner, p4est_connect_type_t balance,
+                            sc_array_t * seeds)
+{
+  p4est_quadrant_t    temp = *p;
+  p4est_quadrant_t   *s;
+  int                 ibalance;
+  int                 consistent;
+
+  P4EST_ASSERT (seeds == NULL ||
+                seeds->elem_size == sizeof (p4est_quadrant_t));
+
+  if (balance == P4EST_CONNECT_FULL) {
+    ibalance = P4EST_DIM - 1;
+  }
+#ifdef P4_TO_P8
+  else if (balance == P8EST_CONNECT_EDGE) {
+    ibalance = 1;
+  }
+#endif
+  else {
+    ibalance = 0;
+  }
+
+  p4est_bal_corner_con_internal (q, &temp, corner, ibalance, &consistent);
+  if (seeds == NULL) {
+    return !consistent;
+  }
+  else {
+    sc_array_resize (seeds, 0);
+    if (!consistent) {
+      sc_array_resize (seeds, seeds->elem_count + 1);
+      s = p4est_quadrant_array_index (seeds, seeds->elem_count - 1);
+
+      *s = temp;
+    }
+
+    return !consistent;
+  }
+}
+
+#ifdef P4_TO_P8
+int
+p8est_balance_seeds_edge (p4est_quadrant_t * q,
+                          p4est_quadrant_t * p,
+                          int edge, p4est_connect_type_t balance,
+                          sc_array_t * seeds)
+{
+  p4est_quadrant_t    temp = *p;
+  p4est_quadrant_t   *s;
+  int                 ibalance;
+  int                 consistent;
+  int                 nextra = 3;
+  p4est_quadrant_t    add[3];
+  int                 i;
+
+  P4EST_ASSERT (seeds == NULL ||
+                seeds->elem_size == sizeof (p4est_quadrant_t));
+
+  if (balance == P4EST_CONNECT_FULL) {
+    ibalance = P4EST_DIM - 1;
+  }
+#ifdef P4_TO_P8
+  else if (balance == P8EST_CONNECT_EDGE) {
+    ibalance = 1;
+  }
+#endif
+  else {
+    ibalance = 0;
+  }
+
+  if (seeds == NULL) {
+    p8est_bal_edge_con_internal (q, &temp, edge, ibalance, &consistent, NULL);
+
+    return !consistent;
+  }
+  else {
+    memset (add, -1, nextra * sizeof (p4est_quadrant_t));
+
+    p8est_bal_edge_con_internal (q, &temp, edge, ibalance, &consistent, add);
+
+    sc_array_resize (seeds, 0);
+    if (!consistent) {
+      for (i = 0; i < nextra; i++) {
+        if (add[i].level != -1) {
+          sc_array_resize (seeds, seeds->elem_count + 1);
+          s = p4est_quadrant_array_index (seeds, seeds->elem_count - 1);
+
+          *s = add[i];
+        }
+      }
+    }
+
+    return !consistent;
+  }
+}
+#endif
+
+int
+p4est_balance_seeds (p4est_quadrant_t * q, p4est_quadrant_t * p,
+                     p4est_connect_type_t balance, sc_array_t * seeds)
+{
+  int                 outside[P4EST_DIM];
+  int                 i;
+  int                 type = 0;
+  p4est_qcoord_t      diff;
+  p4est_qcoord_t      qc, pc;
+  p4est_qcoord_t      pdist = P4EST_QUADRANT_LEN (p->level);
+  p4est_qcoord_t      qdist = P4EST_QUADRANT_LEN (q->level);
+  p4est_quadrant_t   *s;
+  int                 f, c;
+#ifdef P4_TO_P8
+  int                 e;
+#endif
+
+  if (seeds != NULL) {
+    sc_array_resize (seeds, 0);
+  }
+
+  /* basic level comparison */
+  if (q->level <= p->level + 1) {
+    return 0;
+  }
+
+  for (i = 0; i < P4EST_DIM; i++) {
+    switch (i) {
+    case 0:
+      qc = q->x;
+      pc = p->x;
+      break;
+    case 1:
+      qc = q->y;
+      pc = p->y;
+      break;
+#ifdef P4_TO_P8
+    case 2:
+      qc = q->z;
+      pc = p->z;
+      break;
+#endif
+    default:
+      SC_ABORT_NOT_REACHED ();
+      break;
+    }
+    outside[i] = 0;
+    if (qc < pc) {
+      diff = pc - qc;
+      /* insulation layer comparison */
+      if (diff > pdist) {
+        return 0;
+      }
+      outside[i] = -1;
+    }
+    else {
+      diff = (qc + qdist) - (pc + pdist);
+      /* insulation layer comparison */
+      if (diff > pdist) {
+        return 0;
+      }
+      if (diff > 0) {
+        outside[i] = 1;
+      }
+    }
+    type += (outside[i] ? 1 : 0);
+  }
+
+  switch (type) {
+  case 0:
+    /* q is inside p, so it is its own seed */
+    sc_array_resize (seeds, seeds->elem_count + 1);
+    s = p4est_quadrant_array_index (seeds, seeds->elem_count - 1);
+    *s = *q;
+    return 1;
+  case 1:
+    for (i = 0; i < P4EST_DIM; i++) {
+      if (outside[i]) {
+        f = 2 * i + (outside[i] > 0 ? 1 : 0);
+        return p4est_balance_seeds_face (q, p, f, balance, seeds);
+      }
+    }
+    SC_ABORT_NOT_REACHED ();
+    return -1;
+  case P4EST_DIM:
+    c = 0;
+    for (i = 0; i < P4EST_DIM; i++) {
+      c += (outside[i] > 0 ? (1 << i) : 0);
+    }
+    return p4est_balance_seeds_corner (q, p, c, balance, seeds);
+#ifdef P4_TO_P8
+  case 2:
+    e = 0;
+    c = 0;
+    for (i = 2; i >= 0; i--) {
+      if (outside[i]) {
+        c <<= 1;
+        c |= (outside[i] > 0 ? 1 : 0);
+      }
+      else {
+        e |= (i << 2);
+      }
+    }
+    e |= c;
+    return p8est_balance_seeds_edge (q, p, e, balance, seeds);
+#endif
+  default:
+    SC_ABORT_NOT_REACHED ();
+    return -1;
+  }
+}
diff --git a/src/p4est_balance.h b/src/p4est_balance.h
new file mode 100644
index 0000000..ce92035
--- /dev/null
+++ b/src/p4est_balance.h
@@ -0,0 +1,64 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2011 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_BALANCE_H
+#define P4EST_BALANCE_H
+
+#include <p4est.h>
+
+/** Determines if quadrant \a q causes quadrant \a p to split under the given
+ * \a balance condition.
+ *
+ * \param [in] q         Test quadrant.
+ * \param [in] p         Trial quadrant.
+ * \param [in] balance   Balance condition.
+ * \param [out] seeds    optional array: if \a seeds is not NULL, then it will
+ *                       be resized and filled with descendants of \a p such
+ *                       that the coarsest balanced subtree rooted at \a p
+ *                       that contains all of \a seeds is also the coarset
+ *                       subtree rooted at \a p that is entirely balanced with
+ *                       \a q.
+ * \return               True if \a q causes \a p to split.
+ */
+int                 p4est_balance_seeds (p4est_quadrant_t * q,
+                                         p4est_quadrant_t * p,
+                                         p4est_connect_type_t balance,
+                                         sc_array_t * seeds);
+
+/** Same as p4est_balance_seeds, optimized for the case when it is already
+ * known that \a q is outside of a certain \a face of \a p.
+ */
+int                 p4est_balance_seeds_face (p4est_quadrant_t * q,
+                                              p4est_quadrant_t * p,
+                                              int face, p4est_connect_type_t
+                                              balance, sc_array_t * seeds);
+
+/** Same as p4est_balance_seeds, optimized for the case when it is already
+ * known that \a q is outside of a certain \a corner of \a p.
+ */
+int                 p4est_balance_seeds_corner (p4est_quadrant_t * q,
+                                                p4est_quadrant_t * p,
+                                                int face, p4est_connect_type_t
+                                                balance, sc_array_t * seeds);
+
+#endif
diff --git a/src/p4est_base.c b/src/p4est_base.c
new file mode 100644
index 0000000..e414f9c
--- /dev/null
+++ b/src/p4est_base.c
@@ -0,0 +1,123 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_base.h>
+
+int                 p4est_package_id = -1;
+
+void
+p4est_init (sc_log_handler_t log_handler, int log_threshold)
+{
+  int                 w;
+
+  p4est_package_id = sc_package_register (log_handler, log_threshold,
+                                          "p4est", "A forest of octrees");
+
+  w = 24;
+  P4EST_GLOBAL_ESSENTIALF ("This is %s\n", P4EST_PACKAGE_STRING);
+  P4EST_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "CPP", P4EST_CPP);
+  P4EST_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "CPPFLAGS", P4EST_CPPFLAGS);
+  P4EST_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "CC", P4EST_CC);
+#if 0
+  P4EST_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "C_VERSION", P4EST_C_VERSION);
+#endif
+  P4EST_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "CFLAGS", P4EST_CFLAGS);
+  P4EST_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "LDFLAGS", P4EST_LDFLAGS);
+  P4EST_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "LIBS", P4EST_LIBS);
+}
+
+#ifndef __cplusplus
+#undef P4EST_GLOBAL_LOGF
+#undef P4EST_LOGF
+#undef P4EST_GLOBAL_TRACEF
+#undef P4EST_GLOBAL_LDEBUGF
+#undef P4EST_GLOBAL_VERBOSEF
+#undef P4EST_GLOBAL_INFOF
+#undef P4EST_GLOBAL_STATISTICSF
+#undef P4EST_GLOBAL_PRODUCTIONF
+#undef P4EST_GLOBAL_ESSENTIALF
+#undef P4EST_GLOBAL_LERRORF
+#undef P4EST_TRACEF
+#undef P4EST_LDEBUGF
+#undef P4EST_VERBOSEF
+#undef P4EST_INFOF
+#undef P4EST_STATISTICSF
+#undef P4EST_PRODUCTIONF
+#undef P4EST_ESSENTIALF
+#undef P4EST_LERRORF
+#endif
+
+#ifndef SC_SPLINT
+
+void
+P4EST_GLOBAL_LOGF (int priority, const char *fmt, ...)
+{
+  va_list             ap;
+
+  va_start (ap, fmt);
+  sc_logv ("<unknown>", 0, p4est_package_id, SC_LC_GLOBAL, priority, fmt, ap);
+  va_end (ap);
+}
+
+void
+P4EST_LOGF (int priority, const char *fmt, ...)
+{
+  va_list             ap;
+
+  va_start (ap, fmt);
+  sc_logv ("<unknown>", 0, p4est_package_id, SC_LC_NORMAL, priority, fmt, ap);
+  va_end (ap);
+}
+
+#define P4EST_LOG_IMP(n,p)                              \
+  void                                                  \
+  P4EST_GLOBAL_ ## n ## F (const char *fmt, ...)        \
+  {                                                     \
+    va_list             ap;                             \
+    va_start (ap, fmt);                                 \
+    sc_logv ("<unknown>", 0, p4est_package_id,          \
+             SC_LC_GLOBAL, SC_LP_ ## p, fmt, ap);       \
+    va_end (ap);                                        \
+  }                                                     \
+  void                                                  \
+  P4EST_ ## n ## F (const char *fmt, ...)               \
+  {                                                     \
+    va_list             ap;                             \
+    va_start (ap, fmt);                                 \
+    sc_logv ("<unknown>", 0, p4est_package_id,          \
+             SC_LC_NORMAL, SC_LP_ ## p, fmt, ap);       \
+    va_end (ap);                                        \
+  }
+
+/* *INDENT-OFF* */
+P4EST_LOG_IMP (TRACE, TRACE)
+P4EST_LOG_IMP (LDEBUG, DEBUG)
+P4EST_LOG_IMP (VERBOSE, VERBOSE)
+P4EST_LOG_IMP (INFO, INFO)
+P4EST_LOG_IMP (STATISTICS, STATISTICS)
+P4EST_LOG_IMP (PRODUCTION, PRODUCTION)
+P4EST_LOG_IMP (ESSENTIAL, ESSENTIAL)
+P4EST_LOG_IMP (LERROR, ERROR)
+/* *INDENT-ON* */
+
+#endif
diff --git a/src/p4est_base.h b/src/p4est_base.h
new file mode 100644
index 0000000..636a7f3
--- /dev/null
+++ b/src/p4est_base.h
@@ -0,0 +1,442 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_base.h
+ *
+ * General support types and functions
+ */
+
+#ifndef P4EST_BASE_H
+#define P4EST_BASE_H
+
+/* include config headers */
+#include <p4est_config.h>
+#include <sc_config.h>
+#if \
+  (defined (P4EST_ENABLE_MPI) && !defined (SC_ENABLE_MPI)) || \
+  (!defined (P4EST_ENABLE_MPI) && defined (SC_ENABLE_MPI))
+#error "MPI configured differently in p4est and libsc"
+#endif
+#if \
+  (defined (P4EST_ENABLE_MPIIO) && !defined (SC_ENABLE_MPIIO)) || \
+  (!defined (P4EST_ENABLE_MPIIO) && defined (SC_ENABLE_MPIIO))
+#error "MPI I/O configured differently in p4est and libsc"
+#endif
+
+/* indirectly also include sc.h */
+#include <sc_containers.h>
+#define _p4est_const _sc_const
+
+SC_EXTERN_C_BEGIN;
+
+/** Typedef for quadrant coordinates. */
+typedef int32_t     p4est_qcoord_t;
+#define p4est_qcoord_compare sc_int32_compare
+#define P4EST_MPI_QCOORD sc_MPI_INT
+#define P4EST_VTK_QCOORD "Int32"
+#define P4EST_F90_QCOORD INTEGER(KIND=C_INT32_T)
+#define P4EST_QCOORD_MIN INT32_MIN
+#define P4EST_QCOORD_MAX INT32_MAX
+#define P4EST_QCOORD_1   ((p4est_qcoord_t) 1)
+
+/** Typedef for counting topological entities (trees, tree vertices). */
+typedef int32_t     p4est_topidx_t;
+#define p4est_topidx_compare sc_int32_compare
+#define P4EST_MPI_TOPIDX sc_MPI_INT
+#define P4EST_VTK_TOPIDX "Int32"
+#define P4EST_F90_TOPIDX INTEGER(KIND=C_INT32_T)
+#define P4EST_TOPIDX_MIN INT32_MIN
+#define P4EST_TOPIDX_MAX INT32_MAX
+#define P4EST_TOPIDX_FITS_32 1
+#define P4EST_TOPIDX_1   ((p4est_topidx_t) 1)
+
+/** Typedef for processor-local indexing of quadrants and nodes. */
+typedef int32_t     p4est_locidx_t;
+#define p4est_locidx_compare sc_int32_compare
+#define P4EST_MPI_LOCIDX sc_MPI_INT
+#define P4EST_VTK_LOCIDX "Int32"
+#define P4EST_F90_LOCIDX INTEGER(KIND=C_INT32_T)
+#define P4EST_LOCIDX_MIN INT32_MIN
+#define P4EST_LOCIDX_MAX INT32_MAX
+#define P4EST_LOCIDX_1   ((p4est_locidx_t) 1)
+
+/** Typedef for globally unique indexing of quadrants. */
+typedef int64_t     p4est_gloidx_t;
+#define p4est_gloidx_compare sc_int64_compare
+#define P4EST_MPI_GLOIDX sc_MPI_LONG_LONG_INT
+#define P4EST_VTK_GLOIDX "Int64"
+#define P4EST_F90_GLOIDX INTEGER(KIND=C_INT64_T)
+#define P4EST_GLOIDX_MIN INT64_MIN
+#define P4EST_GLOIDX_MAX INT64_MAX
+#define P4EST_GLOIDX_1   ((p4est_gloidx_t) 1)
+
+/* some error checking possibly specific to p4est */
+#ifdef P4EST_ENABLE_DEBUG
+#define P4EST_ASSERT(c) SC_CHECK_ABORT ((c), "Assertion '" #c "'")
+#define P4EST_EXECUTE_ASSERT_FALSE(expression)                          \
+  do { int _p4est_i = (int) (expression);                               \
+       SC_CHECK_ABORT (!_p4est_i, "Expected false: '" #expression "'"); \
+  } while (0)
+#define P4EST_EXECUTE_ASSERT_TRUE(expression)                           \
+  do { int _p4est_i = (int) (expression);                               \
+       SC_CHECK_ABORT (_p4est_i, "Expected true: '" #expression "'");   \
+  } while (0)
+#else
+#define P4EST_ASSERT(c) SC_NOOP ()
+#define P4EST_EXECUTE_ASSERT_FALSE(expression) \
+  do { (void) (expression); } while (0)
+#define P4EST_EXECUTE_ASSERT_TRUE(expression) \
+  do { (void) (expression); } while (0)
+#endif
+
+/* macros for memory allocation, will abort if out of memory */
+/** allocate a \a t-array with \a n elements */
+#define P4EST_ALLOC(t,n)          (t *) sc_malloc (p4est_package_id,    \
+                                                   (n) * sizeof(t))
+/** allocate a \a t-array with \a n elements and zero */
+#define P4EST_ALLOC_ZERO(t,n)     (t *) sc_calloc (p4est_package_id,    \
+                                                   (size_t) (n), sizeof(t))
+/** reallocate the \a t-array \a p with \a n elements */
+#define P4EST_REALLOC(p,t,n)      (t *) sc_realloc (p4est_package_id,   \
+                                                    (p), (n) * sizeof(t))
+/** duplicate a string */
+#define P4EST_STRDUP(s)                 sc_strdup (p4est_package_id, (s))
+/** free an allocated array */
+#define P4EST_FREE(p)                   sc_free (p4est_package_id, (p))
+
+/* log helper macros */
+#define P4EST_GLOBAL_LOG(p,s)                           \
+  SC_GEN_LOG (p4est_package_id, SC_LC_GLOBAL, (p), (s))
+#define P4EST_LOG(p,s)                                  \
+  SC_GEN_LOG (p4est_package_id, SC_LC_NORMAL, (p), (s))
+void                P4EST_GLOBAL_LOGF (int priority, const char *fmt, ...)
+  __attribute__ ((format (printf, 2, 3)));
+void                P4EST_LOGF (int priority, const char *fmt, ...)
+  __attribute__ ((format (printf, 2, 3)));
+#ifndef __cplusplus
+#define P4EST_GLOBAL_LOGF(p,f,...)                                      \
+  SC_GEN_LOGF (p4est_package_id, SC_LC_GLOBAL, (p), (f), __VA_ARGS__)
+#define P4EST_LOGF(p,f,...)                                             \
+  SC_GEN_LOGF (p4est_package_id, SC_LC_NORMAL, (p), (f), __VA_ARGS__)
+#endif
+
+/* convenience global log macros will only print if identifier <= 0 */
+#define P4EST_GLOBAL_TRACE(s) P4EST_GLOBAL_LOG (SC_LP_TRACE, (s))
+#define P4EST_GLOBAL_LDEBUG(s) P4EST_GLOBAL_LOG (SC_LP_DEBUG, (s))
+#define P4EST_GLOBAL_VERBOSE(s) P4EST_GLOBAL_LOG (SC_LP_VERBOSE, (s))
+#define P4EST_GLOBAL_INFO(s) P4EST_GLOBAL_LOG (SC_LP_INFO, (s))
+#define P4EST_GLOBAL_STATISTICS(s) P4EST_GLOBAL_LOG (SC_LP_STATISTICS, (s))
+#define P4EST_GLOBAL_PRODUCTION(s) P4EST_GLOBAL_LOG (SC_LP_PRODUCTION, (s))
+#define P4EST_GLOBAL_ESSENTIAL(s) P4EST_GLOBAL_LOG (SC_LP_ESSENTIAL, (s))
+#define P4EST_GLOBAL_LERROR(s) P4EST_GLOBAL_LOG (SC_LP_ERROR, (s))
+void                P4EST_GLOBAL_TRACEF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_GLOBAL_LDEBUGF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_GLOBAL_VERBOSEF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_GLOBAL_INFOF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_GLOBAL_STATISTICSF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_GLOBAL_PRODUCTIONF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_GLOBAL_ESSENTIALF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_GLOBAL_LERRORF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+#ifndef __cplusplus
+#define P4EST_GLOBAL_TRACEF(f,...)                      \
+  P4EST_GLOBAL_LOGF (SC_LP_TRACE, (f), __VA_ARGS__)
+#define P4EST_GLOBAL_LDEBUGF(f,...)                     \
+  P4EST_GLOBAL_LOGF (SC_LP_DEBUG, (f), __VA_ARGS__)
+#define P4EST_GLOBAL_VERBOSEF(f,...)                    \
+  P4EST_GLOBAL_LOGF (SC_LP_VERBOSE, (f), __VA_ARGS__)
+#define P4EST_GLOBAL_INFOF(f,...)                       \
+  P4EST_GLOBAL_LOGF (SC_LP_INFO, (f), __VA_ARGS__)
+#define P4EST_GLOBAL_STATISTICSF(f,...)                         \
+  P4EST_GLOBAL_LOGF (SC_LP_STATISTICS, (f), __VA_ARGS__)
+#define P4EST_GLOBAL_PRODUCTIONF(f,...)                         \
+  P4EST_GLOBAL_LOGF (SC_LP_PRODUCTION, (f), __VA_ARGS__)
+#define P4EST_GLOBAL_ESSENTIALF(f,...)                          \
+  P4EST_GLOBAL_LOGF (SC_LP_ESSENTIAL, (f), __VA_ARGS__)
+#define P4EST_GLOBAL_LERRORF(f,...)                     \
+  P4EST_GLOBAL_LOGF (SC_LP_ERROR, (f), __VA_ARGS__)
+#endif
+#define P4EST_GLOBAL_NOTICE     P4EST_GLOBAL_STATISTICS
+#define P4EST_GLOBAL_NOTICEF    P4EST_GLOBAL_STATISTICSF
+
+/* convenience log macros that are active on every processor */
+#define P4EST_TRACE(s) P4EST_LOG (SC_LP_TRACE, (s))
+#define P4EST_LDEBUG(s) P4EST_LOG (SC_LP_DEBUG, (s))
+#define P4EST_VERBOSE(s) P4EST_LOG (SC_LP_VERBOSE, (s))
+#define P4EST_INFO(s) P4EST_LOG (SC_LP_INFO, (s))
+#define P4EST_STATISTICS(s) P4EST_LOG (SC_LP_STATISTICS, (s))
+#define P4EST_PRODUCTION(s) P4EST_LOG (SC_LP_PRODUCTION, (s))
+#define P4EST_ESSENTIAL(s) P4EST_LOG (SC_LP_ESSENTIAL, (s))
+#define P4EST_LERROR(s) P4EST_LOG (SC_LP_ERROR, (s))
+void                P4EST_TRACEF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_LDEBUGF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_VERBOSEF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_INFOF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_STATISTICSF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_PRODUCTIONF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_ESSENTIALF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+void                P4EST_LERRORF (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+#ifndef __cplusplus
+#define P4EST_TRACEF(f,...)                     \
+  P4EST_LOGF (SC_LP_TRACE, (f), __VA_ARGS__)
+#define P4EST_LDEBUGF(f,...)                    \
+  P4EST_LOGF (SC_LP_DEBUG, (f), __VA_ARGS__)
+#define P4EST_VERBOSEF(f,...)                   \
+  P4EST_LOGF (SC_LP_VERBOSE, (f), __VA_ARGS__)
+#define P4EST_INFOF(f,...)                      \
+  P4EST_LOGF (SC_LP_INFO, (f), __VA_ARGS__)
+#define P4EST_STATISTICSF(f,...)                        \
+  P4EST_LOGF (SC_LP_STATISTICS, (f), __VA_ARGS__)
+#define P4EST_PRODUCTIONF(f,...)                        \
+  P4EST_LOGF (SC_LP_PRODUCTION, (f), __VA_ARGS__)
+#define P4EST_ESSENTIALF(f,...)                         \
+  P4EST_LOGF (SC_LP_ESSENTIAL, (f), __VA_ARGS__)
+#define P4EST_LERRORF(f,...)                    \
+  P4EST_LOGF (SC_LP_ERROR, (f), __VA_ARGS__)
+#endif
+#define P4EST_NOTICE            P4EST_STATISTICS
+#define P4EST_NOTICEF           P4EST_STATISTICSF
+
+/* extern declarations */
+/** the libsc package id for p4est (set in p4est_init()) */
+extern int          p4est_package_id;
+
+static inline void
+p4est_log_indent_push ()
+{
+  sc_log_indent_push_count (p4est_package_id, 1);
+}
+
+static inline void
+p4est_log_indent_pop ()
+{
+  sc_log_indent_pop_count (p4est_package_id, 1);
+}
+
+/** Registers p4est with the SC Library and sets the logging behavior.
+ * This function is optional.
+ * This function must only be called before additional threads are created.
+ * If this function is not called or called with log_handler == NULL,
+ * the default SC log handler will be used.
+ * If this function is not called or called with log_threshold == SC_LP_DEFAULT,
+ * the default SC log threshold will be used.
+ * The default SC log settings can be changed with sc_set_log_defaults ().
+ */
+void                p4est_init (sc_log_handler_t log_handler,
+                                int log_threshold);
+
+/** Compute hash value for two p4est_topidx_t integers.
+ * \param [in] tt     Array of (at least) two values.
+ * \return            An unsigned hash value.
+ */
+/*@unused@*/
+static inline unsigned
+p4est_topidx_hash2 (const p4est_topidx_t * tt)
+{
+  uint32_t            a, b, c;
+
+#if (P4EST_TOPIDX_FITS_32)
+  a = (uint32_t) tt[0];
+  b = (uint32_t) tt[1];
+  c = 0;
+#else
+  a = (uint32_t) (tt[0] && 0xFFFFFFFF);
+  b = (uint32_t) (tt[0] >> 32);
+  c = (uint32_t) (tt[1] && 0xFFFFFFFF);
+  sc_hash_mix (a, b, c);
+  a += (uint32_t) (tt[1] >> 32);
+#endif
+  sc_hash_final (a, b, c);
+
+  return (unsigned) c;
+}
+
+/** Compute hash value for three p4est_topidx_t integers.
+ * \param [in] tt     Array of (at least) three values.
+ * \return            An unsigned hash value.
+ */
+/*@unused@*/
+static inline unsigned
+p4est_topidx_hash3 (const p4est_topidx_t * tt)
+{
+  uint32_t            a, b, c;
+
+#if (P4EST_TOPIDX_FITS_32)
+  a = (uint32_t) tt[0];
+  b = (uint32_t) tt[1];
+  c = (uint32_t) tt[2];
+#else
+  a = (uint32_t) (tt[0] && 0xFFFFFFFF);
+  b = (uint32_t) (tt[0] >> 32);
+  c = (uint32_t) (tt[1] && 0xFFFFFFFF);
+  sc_hash_mix (a, b, c);
+  a += (uint32_t) (tt[1] >> 32);
+  b += (uint32_t) (tt[2] && 0xFFFFFFFF);
+  c += (uint32_t) (tt[2] >> 32);
+#endif
+  sc_hash_final (a, b, c);
+
+  return (unsigned) c;
+}
+
+/** Compute hash value for four p4est_topidx_t integers.
+ * \param [in] tt     Array of (at least) four values.
+ * \return            An unsigned hash value.
+ */
+/*@unused@*/
+static inline unsigned
+p4est_topidx_hash4 (const p4est_topidx_t * tt)
+{
+  uint32_t            a, b, c;
+
+#if (P4EST_TOPIDX_FITS_32)
+  a = (uint32_t) tt[0];
+  b = (uint32_t) tt[1];
+  c = (uint32_t) tt[2];
+  sc_hash_mix (a, b, c);
+  a += (uint32_t) tt[3];
+#else
+  a = (uint32_t) (tt[0] && 0xFFFFFFFF);
+  b = (uint32_t) (tt[0] >> 32);
+  c = (uint32_t) (tt[1] && 0xFFFFFFFF);
+  sc_hash_mix (a, b, c);
+  a += (uint32_t) (tt[1] >> 32);
+  b += (uint32_t) (tt[2] && 0xFFFFFFFF);
+  c += (uint32_t) (tt[2] >> 32);
+  sc_hash_mix (a, b, c);
+  a += (uint32_t) (tt[3] && 0xFFFFFFFF);
+  b += (uint32_t) (tt[3] >> 32);
+#endif
+  sc_hash_final (a, b, c);
+
+  return (unsigned) c;
+}
+
+/*@unused@*/
+static inline int
+p4est_topidx_is_sorted (p4est_topidx_t * t, int length)
+{
+  int                 i;
+
+  for (i = 1; i < length; ++i) {
+    if (t[i - 1] > t[i]) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+/*@unused@*/
+static inline void
+p4est_topidx_bsort (p4est_topidx_t * t, int length)
+{
+  int                 i, j;
+  p4est_topidx_t      tswap;
+
+  /* go through all elements except the last */
+  for (i = length - 1; i > 0; --i) {
+    /* bubble up the first element until before position i */
+    for (j = 0; j < i; ++j) {
+      if (t[j] > t[j + 1]) {
+        tswap = t[j + 1];
+        t[j + 1] = t[j];
+        t[j] = tswap;
+      }
+    }
+  }
+  P4EST_ASSERT (p4est_topidx_is_sorted (t, length));
+}
+
+/*@unused@*/
+static inline       uint64_t
+p4est_partition_cut_uint64 (uint64_t global_num, int p, int num_procs)
+{
+  uint64_t            result;
+
+  /* In theory, a double * double product should never overflow
+     due to the 15-bit exponent used internally on x87 and above.
+     Also in theory, 80-bit floats should be used internally,
+     and multiply/divide associativity goes left-to-right.
+     Still checking for funny stuff just to be sure. */
+
+  P4EST_ASSERT (0 <= p && p <= num_procs);
+
+  if (p == num_procs) {
+    /* prevent roundoff error and division by zero */
+    return global_num;
+  }
+
+  result = (uint64_t)
+    (((long double) global_num * (double) p) / (double) num_procs);
+
+  P4EST_ASSERT (result <= global_num);
+
+  return result;
+}
+
+/*@unused@*/
+static inline       p4est_gloidx_t
+p4est_partition_cut_gloidx (p4est_gloidx_t global_num, int p, int num_procs)
+{
+  p4est_gloidx_t      result;
+
+  /* In theory, a double * double product should never overflow
+     due to the 15-bit exponent used internally on x87 and above.
+     Also in theory, 80-bit floats should be used internally,
+     and multiply/divide associativity goes left-to-right.
+     Still checking for funny stuff just to be sure. */
+
+  P4EST_ASSERT (global_num >= 0);
+  P4EST_ASSERT (0 <= p && p <= num_procs);
+
+  if (p == num_procs) {
+    /* prevent roundoff error and division by zero */
+    return global_num;
+  }
+
+  result = (p4est_gloidx_t)
+    (((long double) global_num * (double) p) / (double) num_procs);
+
+  P4EST_ASSERT (0 <= result && result <= global_num);
+
+  return result;
+}
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_BASE_H */
diff --git a/src/p4est_bits.c b/src/p4est_bits.c
new file mode 100644
index 0000000..da398e8
--- /dev/null
+++ b/src/p4est_bits.c
@@ -0,0 +1,1776 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef P4_TO_P8
+#include <p8est_bits.h>
+#else
+#include <p4est_bits.h>
+#endif /* !P4_TO_P8 */
+
+void
+p4est_quadrant_print (int log_priority, const p4est_quadrant_t * q)
+{
+#ifdef P4_TO_P8
+  P4EST_LOGF (log_priority, "x 0x%x y 0x%x z 0x%x level %d\n",
+              q->x, q->y, q->z, q->level);
+#else
+  P4EST_LOGF (log_priority, "x 0x%x y 0x%x level %d\n", q->x, q->y, q->level);
+#endif
+}
+
+int
+p4est_quadrant_is_equal (const p4est_quadrant_t * q1,
+                         const p4est_quadrant_t * q2)
+{
+  P4EST_ASSERT (p4est_quadrant_is_node (q1, 1) ||
+                p4est_quadrant_is_extended (q1));
+  P4EST_ASSERT (p4est_quadrant_is_node (q2, 1) ||
+                p4est_quadrant_is_extended (q2));
+
+  return (q1->level == q2->level && q1->x == q2->x && q1->y == q2->y)
+#ifdef P4_TO_P8
+    && (q1->z == q2->z)
+#endif
+    ;
+}
+
+int
+p4est_quadrant_overlaps (const p4est_quadrant_t * q1,
+                         const p4est_quadrant_t * q2)
+{
+  int8_t              level = SC_MIN (q1->level, q2->level);
+  p4est_qcoord_t      mask =
+    ((p4est_qcoord_t) - 1) << (P4EST_MAXLEVEL - level);
+
+  if (((q1->x ^ q2->x) & mask) || ((q1->y ^ q2->y) & mask)
+#ifdef P4_TO_P8
+      || ((q1->z ^ q2->z) & mask)
+#endif
+      || 0) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int
+p4est_quadrant_is_equal_piggy (const p4est_quadrant_t * q1,
+                               const p4est_quadrant_t * q2)
+{
+  return
+    q1->p.which_tree == q2->p.which_tree && p4est_quadrant_is_equal (q1, q2);
+}
+
+int
+p4est_quadrant_compare (const void *v1, const void *v2)
+{
+  const p4est_quadrant_t *q1 = (const p4est_quadrant_t *) v1;
+  const p4est_quadrant_t *q2 = (const p4est_quadrant_t *) v2;
+
+  uint32_t            exclorx, exclory, exclorxy, exclor;
+#ifdef P4_TO_P8
+  uint32_t            exclorz;
+#endif
+  int64_t             p1, p2, diff;
+
+  P4EST_ASSERT (p4est_quadrant_is_node (q1, 1) ||
+                p4est_quadrant_is_extended (q1));
+  P4EST_ASSERT (p4est_quadrant_is_node (q2, 1) ||
+                p4est_quadrant_is_extended (q2));
+
+  /* these are unsigned variables that inherit the sign bits */
+  exclorx = q1->x ^ q2->x;
+  exclory = q1->y ^ q2->y;
+  exclor = exclorxy = exclorx | exclory;
+#ifdef P4_TO_P8
+  exclorz = q1->z ^ q2->z;
+  exclor = exclorxy | exclorz;
+#endif
+
+  if (!exclor) {
+    return (int) q1->level - (int) q2->level;
+  }
+
+#ifdef P4_TO_P8
+  /* if (exclor ^ exclorz) > exclorz, then exclorxy has a more significant bit
+   * than exclorz; also exclor and (exclor ^ exclorz) cannot be equal */
+  if (exclorz > (exclor ^ exclorz)) {
+    p1 = q1->z + ((q1->z >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2)));
+    p2 = q2->z + ((q2->z >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2)));
+  }
+  else
+#if 0
+    ;
+#endif
+#endif
+  if (exclory > (exclorxy ^ exclory)) {
+    p1 = q1->y + ((q1->y >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2)));
+    p2 = q2->y + ((q2->y >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2)));
+  }
+  else {
+    p1 = q1->x + ((q1->x >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2)));
+    p2 = q2->x + ((q2->x >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2)));
+  }
+  diff = p1 - p2;
+  return (diff == 0) ? 0 : ((diff < 0) ? -1 : 1);
+}
+
+int
+p4est_quadrant_disjoint (const void *a, const void *b)
+{
+  const p4est_quadrant_t *q = (p4est_quadrant_t *) a;
+  const p4est_quadrant_t *r = (p4est_quadrant_t *) b;
+  int8_t              level = SC_MIN (q->level, r->level);
+  p4est_qcoord_t      mask =
+    ((p4est_qcoord_t) - 1) << (P4EST_MAXLEVEL - level);
+
+  if (((q->x ^ r->x) & mask) || ((q->y ^ r->y) & mask)
+#ifdef P4_TO_P8
+      || ((q->z ^ r->z) & mask)
+#endif
+      || 0) {
+    return p4est_quadrant_compare (a, b);
+  }
+
+  return 0;
+}
+
+int
+p4est_quadrant_compare_piggy (const void *v1, const void *v2)
+{
+  const p4est_quadrant_t *q1 = (const p4est_quadrant_t *) v1;
+  const p4est_quadrant_t *q2 = (const p4est_quadrant_t *) v2;
+
+  /* expect non-negative tree information */
+  /* *INDENT-OFF* horrible indent bug */
+  const p4est_topidx_t diff =
+    q1->p.which_tree - q2->p.which_tree;        /* same type */
+  /* *INDENT-ON* */
+
+  P4EST_ASSERT (q1->p.which_tree >= 0 && q2->p.which_tree >= 0);
+
+  return (diff == 0) ?
+    p4est_quadrant_compare (v1, v2) : ((diff < 0) ? -1 : 1);
+}
+
+int
+p4est_quadrant_compare_local_num (const void *v1, const void *v2)
+{
+  const p4est_quadrant_t *q1 = (const p4est_quadrant_t *) v1;
+  const p4est_quadrant_t *q2 = (const p4est_quadrant_t *) v2;
+
+  return p4est_locidx_compare (&q1->p.piggy3.local_num,
+                               &q2->p.piggy3.local_num);
+}
+
+int
+p4est_quadrant_equal_fn (const void *v1, const void *v2, const void *u)
+{
+  const p4est_quadrant_t *q1 = (const p4est_quadrant_t *) v1;
+  const p4est_quadrant_t *q2 = (const p4est_quadrant_t *) v2;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q1));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q2));
+
+  return q1->level == q2->level && q1->x == q2->x && q1->y == q2->y
+#ifdef P4_TO_P8
+    && q1->z == q2->z
+#endif
+    ;
+}
+
+unsigned
+p4est_quadrant_hash_fn (const void *v, const void *u)
+{
+  const p4est_quadrant_t *q = (const p4est_quadrant_t *) v;
+  uint32_t            a, b, c;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+
+  a = (uint32_t) q->x;
+  b = (uint32_t) q->y;
+#ifndef P4_TO_P8
+  c = (uint32_t) q->level;
+#else
+  c = (uint32_t) q->z;
+  sc_hash_mix (a, b, c);
+  a += (uint32_t) q->level;
+#endif
+  sc_hash_final (a, b, c);
+
+  return (unsigned) c;
+}
+
+int
+p4est_node_equal_piggy_fn (const void *v1, const void *v2, const void *u)
+{
+  const p4est_quadrant_t *q1 = (const p4est_quadrant_t *) v1;
+  const p4est_quadrant_t *q2 = (const p4est_quadrant_t *) v2;
+#ifdef P4EST_ENABLE_DEBUG
+  const int           clamped = *(int *) u;
+#endif
+
+  P4EST_ASSERT (p4est_quadrant_is_node (q1, clamped));
+  P4EST_ASSERT (p4est_quadrant_is_node (q2, clamped));
+
+  return
+    q1->p.which_tree == q2->p.which_tree && q1->x == q2->x && q1->y == q2->y
+#ifdef P4_TO_P8
+    && q1->z == q2->z
+#endif
+    ;
+}
+
+unsigned
+p4est_node_hash_piggy_fn (const void *v, const void *u)
+{
+  const p4est_quadrant_t *q = (const p4est_quadrant_t *) v;
+#ifdef P4EST_ENABLE_DEBUG
+  const int           clamped = *(int *) u;
+#endif
+  uint32_t            a, b, c;
+
+  P4EST_ASSERT (p4est_quadrant_is_node (q, clamped));
+
+  a = (uint32_t) q->x;
+  b = (uint32_t) q->y;
+#ifndef P4_TO_P8
+  c = (uint32_t) q->p.which_tree;
+#else
+  c = (uint32_t) q->z;
+  sc_hash_mix (a, b, c);
+  a += (uint32_t) q->p.which_tree;
+#endif
+  sc_hash_final (a, b, c);
+
+  return (unsigned) c;
+}
+
+void
+p4est_node_clamp_inside (const p4est_quadrant_t * n, p4est_quadrant_t * r)
+{
+  P4EST_ASSERT (p4est_quadrant_is_node (n, 0));
+
+  r->x = (n->x == P4EST_ROOT_LEN) ? (P4EST_ROOT_LEN - 1) : n->x;
+  r->y = (n->y == P4EST_ROOT_LEN) ? (P4EST_ROOT_LEN - 1) : n->y;
+#ifdef P4_TO_P8
+  r->z = (n->z == P4EST_ROOT_LEN) ? (P4EST_ROOT_LEN - 1) : n->z;
+#endif
+  r->level = P4EST_MAXLEVEL;
+  P4EST_ASSERT (p4est_quadrant_is_node (r, 1));
+}
+
+void
+p4est_node_unclamp (p4est_quadrant_t * n)
+{
+  P4EST_ASSERT (p4est_quadrant_is_node (n, 1));
+
+  if (n->x == P4EST_ROOT_LEN - 1)
+    n->x = P4EST_ROOT_LEN;
+  if (n->y == P4EST_ROOT_LEN - 1)
+    n->y = P4EST_ROOT_LEN;
+#ifdef P4_TO_P8
+  if (n->z == P4EST_ROOT_LEN - 1)
+    n->z = P4EST_ROOT_LEN;
+#endif
+  P4EST_ASSERT (p4est_quadrant_is_node (n, 0));
+}
+
+void
+p4est_node_to_quadrant (const p4est_quadrant_t * n, int level,
+                        p4est_quadrant_t * q)
+{
+  P4EST_ASSERT (p4est_quadrant_is_node (n, 1));
+  P4EST_ASSERT (0 <= level && level <= P4EST_QMAXLEVEL);
+
+  q->x = n->x & ~((1 << (P4EST_MAXLEVEL - level)) - 1);
+  q->y = n->y & ~((1 << (P4EST_MAXLEVEL - level)) - 1);
+#ifdef P4_TO_P8
+  q->z = n->z & ~((1 << (P4EST_MAXLEVEL - level)) - 1);
+#endif
+  q->level = (int8_t) level;
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+}
+
+int
+p4est_quadrant_contains_node (const p4est_quadrant_t * q,
+                              const p4est_quadrant_t * n)
+{
+  const p4est_qcoord_t qlen = P4EST_QUADRANT_LEN (q->level);
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+  P4EST_ASSERT (p4est_quadrant_is_node (n, 1));
+
+  return
+    (q->x <= n->x && n->x < q->x + qlen) &&
+    (q->y <= n->y && n->y < q->y + qlen)
+#ifdef P4_TO_P8
+    && (q->z <= n->z && n->z < q->z + qlen)
+#endif
+    ;
+}
+
+int
+p4est_quadrant_ancestor_id (const p4est_quadrant_t * q, int level)
+{
+  int                 id = 0;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (0 <= level && level <= P4EST_MAXLEVEL);
+  P4EST_ASSERT ((int) q->level >= level);
+
+  if (level == 0) {
+    return 0;
+  }
+
+  id |= ((q->x & P4EST_QUADRANT_LEN (level)) ? 0x01 : 0);
+  id |= ((q->y & P4EST_QUADRANT_LEN (level)) ? 0x02 : 0);
+#ifdef P4_TO_P8
+  id |= ((q->z & P4EST_QUADRANT_LEN (level)) ? 0x04 : 0);
+#endif
+
+  return id;
+}
+
+int
+p4est_quadrant_child_id (const p4est_quadrant_t * q)
+{
+  return p4est_quadrant_ancestor_id (q, (int) q->level);
+}
+
+int
+p4est_quadrant_is_inside_root (const p4est_quadrant_t * q)
+{
+  return
+    (q->x >= 0 && q->x < P4EST_ROOT_LEN) &&
+    (q->y >= 0 && q->y < P4EST_ROOT_LEN) &&
+#ifdef P4_TO_P8
+    (q->z >= 0 && q->z < P4EST_ROOT_LEN) &&
+#endif
+    1;
+}
+
+int
+p4est_quadrant_is_inside_3x3 (const p4est_quadrant_t * q)
+{
+  return
+    (q->x >= -P4EST_ROOT_LEN &&
+     q->x <= P4EST_ROOT_LEN + (P4EST_ROOT_LEN - 1)) &&
+    (q->y >= -P4EST_ROOT_LEN &&
+     q->y <= P4EST_ROOT_LEN + (P4EST_ROOT_LEN - 1)) &&
+#ifdef P4_TO_P8
+    (q->z >= -P4EST_ROOT_LEN &&
+     q->z <= P4EST_ROOT_LEN + (P4EST_ROOT_LEN - 1)) &&
+#endif
+    1;
+}
+
+int
+p4est_quadrant_is_outside_face (const p4est_quadrant_t * q)
+{
+  int                 outface[P4EST_DIM];
+
+  outface[0] = (int) (q->x < 0 || q->x >= P4EST_ROOT_LEN);
+  outface[1] = (int) (q->y < 0 || q->y >= P4EST_ROOT_LEN);
+#ifdef P4_TO_P8
+  outface[2] = (int) (q->z < 0 || q->z >= P4EST_ROOT_LEN);
+#endif
+
+  return outface[0] + outface[1]
+#ifdef P4_TO_P8
+    + outface[2]
+#endif
+    == 1;
+}
+
+int
+p4est_quadrant_is_outside_corner (const p4est_quadrant_t * q)
+{
+  return
+    (q->x < 0 || q->x >= P4EST_ROOT_LEN) &&
+    (q->y < 0 || q->y >= P4EST_ROOT_LEN) &&
+#ifdef P4_TO_P8
+    (q->z < 0 || q->z >= P4EST_ROOT_LEN) &&
+#endif
+    1;
+}
+
+int
+p4est_quadrant_is_node (const p4est_quadrant_t * q, int inside)
+{
+  return
+    q->level == P4EST_MAXLEVEL &&
+    q->x >= 0 && q->x <= P4EST_ROOT_LEN - (inside ? 1 : 0) &&
+    q->y >= 0 && q->y <= P4EST_ROOT_LEN - (inside ? 1 : 0) &&
+#ifdef P4_TO_P8
+    q->z >= 0 && q->z <= P4EST_ROOT_LEN - (inside ? 1 : 0) &&
+#endif
+    (!(q->x & ((1 << (P4EST_MAXLEVEL - P4EST_QMAXLEVEL)) - 1))
+     || (inside && q->x == P4EST_ROOT_LEN - 1)) &&
+    (!(q->y & ((1 << (P4EST_MAXLEVEL - P4EST_QMAXLEVEL)) - 1))
+     || (inside && q->y == P4EST_ROOT_LEN - 1)) &&
+#ifdef P4_TO_P8
+    (!(q->z & ((1 << (P4EST_MAXLEVEL - P4EST_QMAXLEVEL)) - 1))
+     || (inside && q->z == P4EST_ROOT_LEN - 1)) &&
+#endif
+    1;
+}
+
+int
+p4est_quadrant_is_valid (const p4est_quadrant_t * q)
+{
+  return
+    (q->level >= 0 && q->level <= P4EST_QMAXLEVEL) &&
+    ((q->x & (P4EST_QUADRANT_LEN (q->level) - 1)) == 0) &&
+    ((q->y & (P4EST_QUADRANT_LEN (q->level) - 1)) == 0) &&
+#ifdef P4_TO_P8
+    ((q->z & (P4EST_QUADRANT_LEN (q->level) - 1)) == 0) &&
+#endif
+    p4est_quadrant_is_inside_root (q);
+}
+
+int
+p4est_quadrant_is_extended (const p4est_quadrant_t * q)
+{
+  return
+    (q->level >= 0 && q->level <= P4EST_QMAXLEVEL) &&
+    ((q->x & (P4EST_QUADRANT_LEN (q->level) - 1)) == 0) &&
+    ((q->y & (P4EST_QUADRANT_LEN (q->level) - 1)) == 0) &&
+#ifdef P4_TO_P8
+    ((q->z & (P4EST_QUADRANT_LEN (q->level) - 1)) == 0) &&
+#endif
+    p4est_quadrant_is_inside_3x3 (q);
+}
+
+int
+p4est_quadrant_is_sibling (const p4est_quadrant_t * q1,
+                           const p4est_quadrant_t * q2)
+{
+  p4est_qcoord_t      exclorx, exclory;
+#ifdef P4_TO_P8
+  p4est_qcoord_t      exclorz;
+#endif
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q1));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q2));
+
+  if (q1->level == 0) {
+    return 0;
+  }
+
+  exclorx = q1->x ^ q2->x;
+  exclory = q1->y ^ q2->y;
+#ifdef P4_TO_P8
+  exclorz = q1->z ^ q2->z;
+
+  if (exclorx == 0 && exclory == 0 && exclorz == 0) {
+    return 0;
+  }
+#else
+  if (exclorx == 0 && exclory == 0) {
+    return 0;
+  }
+#endif
+
+  return
+    (q1->level == q2->level) &&
+    ((exclorx & ~P4EST_QUADRANT_LEN (q1->level)) == 0) &&
+    ((exclory & ~P4EST_QUADRANT_LEN (q1->level)) == 0) &&
+#ifdef P4_TO_P8
+    ((exclorz & ~P4EST_QUADRANT_LEN (q1->level)) == 0) &&
+#endif
+    1;
+}
+
+int
+p4est_quadrant_is_sibling_D (const p4est_quadrant_t * q1,
+                             const p4est_quadrant_t * q2)
+{
+  p4est_quadrant_t    p1, p2;
+
+  /* make sure the quadrant_parent functions below don't abort */
+  if (q1->level == 0) {
+    return 0;
+  }
+
+  /* validity of q1 and q2 is asserted in p4est_quadrant_is_equal */
+  if (p4est_quadrant_is_equal (q1, q2)) {
+    return 0;
+  }
+
+  p4est_quadrant_parent (q1, &p1);
+  p4est_quadrant_parent (q2, &p2);
+
+  return p4est_quadrant_is_equal (&p1, &p2);
+}
+
+#ifndef P4_TO_P8
+
+int
+p4est_quadrant_is_family (const p4est_quadrant_t * q0,
+                          const p4est_quadrant_t * q1,
+                          const p4est_quadrant_t * q2,
+                          const p4est_quadrant_t * q3)
+{
+  const int8_t        level = q0->level;
+  p4est_qcoord_t      inc;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q0));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q1));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q2));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q3));
+
+  if (level == 0 || level != q1->level ||
+      level != q2->level || level != q3->level) {
+    return 0;
+  }
+
+  inc = P4EST_QUADRANT_LEN (level);
+  return ((q0->x + inc == q1->x && q0->y == q1->y) &&
+          (q0->x == q2->x && q0->y + inc == q2->y) &&
+          (q1->x == q3->x && q2->y == q3->y));
+}
+
+#endif /* !P4_TO_P8 */
+
+int
+p4est_quadrant_is_familyv (const p4est_quadrant_t q[])
+{
+  return p4est_quadrant_is_family (&q[0], &q[1], &q[2], &q[3]
+#ifdef P4_TO_P8
+                                   , &q[4], &q[5], &q[6], &q[7]
+#endif
+    );
+}
+
+int
+p4est_quadrant_is_familypv (p4est_quadrant_t * q[])
+{
+  return p4est_quadrant_is_family (q[0], q[1], q[2], q[3]
+#ifdef P4_TO_P8
+                                   , q[4], q[5], q[6], q[7]
+#endif
+    );
+}
+
+int
+p4est_quadrant_is_parent (const p4est_quadrant_t * q,
+                          const p4est_quadrant_t * r)
+{
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+
+  return
+    (q->level + 1 == r->level) &&
+    (q->x == (r->x & ~P4EST_QUADRANT_LEN (r->level))) &&
+    (q->y == (r->y & ~P4EST_QUADRANT_LEN (r->level))) &&
+#ifdef P4_TO_P8
+    (q->z == (r->z & ~P4EST_QUADRANT_LEN (r->level))) &&
+#endif
+    1;
+}
+
+int
+p4est_quadrant_is_parent_D (const p4est_quadrant_t * q,
+                            const p4est_quadrant_t * r)
+{
+  p4est_quadrant_t    p;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+
+  /* make sure the quadrant_parent function below doesn't abort */
+  if (r->level == 0) {
+    return 0;
+  }
+
+  /* validity of r is asserted in p4est_quadrant_parent */
+  p4est_quadrant_parent (r, &p);
+
+  return p4est_quadrant_is_equal (q, &p);
+}
+
+int
+p4est_quadrant_is_ancestor (const p4est_quadrant_t * q,
+                            const p4est_quadrant_t * r)
+{
+  p4est_qcoord_t      exclorx;
+  p4est_qcoord_t      exclory;
+#ifdef P4_TO_P8
+  p4est_qcoord_t      exclorz;
+#endif
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+
+  if (q->level >= r->level) {
+    return 0;
+  }
+
+  exclorx = (q->x ^ r->x) >> (P4EST_MAXLEVEL - q->level);
+  exclory = (q->y ^ r->y) >> (P4EST_MAXLEVEL - q->level);
+#ifdef P4_TO_P8
+  exclorz = (q->z ^ r->z) >> (P4EST_MAXLEVEL - q->level);
+
+  return (exclorx == 0 && exclory == 0 && exclorz == 0);
+#else
+  return (exclorx == 0 && exclory == 0);
+#endif
+}
+
+int
+p4est_quadrant_is_ancestor_D (const p4est_quadrant_t * q,
+                              const p4est_quadrant_t * r)
+{
+  p4est_quadrant_t    s;
+
+  /* validity of q and r is asserted in p4est_quadrant_is_equal */
+  if (p4est_quadrant_is_equal (q, r)) {
+    return 0;
+  }
+
+  /* this will abort if q and r are in different trees */
+  p4est_nearest_common_ancestor_D (q, r, &s);
+
+  return p4est_quadrant_is_equal (q, &s);
+}
+
+int
+p4est_quadrant_is_next (const p4est_quadrant_t * q,
+                        const p4est_quadrant_t * r)
+{
+  int                 minlevel;
+  uint64_t            i1, i2;
+  p4est_qcoord_t      mask;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+
+  /* the condition q < r is checked implicitly */
+
+  if (q->level > r->level) {
+    /* check if q is the last child up to the common level */
+    mask = P4EST_QUADRANT_LEN (r->level) - P4EST_QUADRANT_LEN (q->level);
+    if ((q->x & mask) != mask || (q->y & mask) != mask ||
+#ifdef P4_TO_P8
+        (q->z & mask) != mask ||
+#endif
+        0) {
+      return 0;
+    }
+    minlevel = (int) r->level;
+  }
+  else {
+    minlevel = (int) q->level;
+  }
+  i1 = p4est_quadrant_linear_id (q, minlevel);
+  i2 = p4est_quadrant_linear_id (r, minlevel);
+
+  return (i1 + 1 == i2);
+}
+
+int
+p4est_quadrant_is_next_D (const p4est_quadrant_t * q,
+                          const p4est_quadrant_t * r)
+{
+  uint64_t            i1, i2;
+  p4est_quadrant_t    a, b;
+
+  /* validity of q and r is asserted in p4est_quadrant_compare */
+  if (p4est_quadrant_compare (q, r) >= 0) {
+    return 0;
+  }
+
+  a = *q;
+  b = *r;
+  while (a.level > b.level) {
+    if (p4est_quadrant_child_id (&a) != P4EST_CHILDREN - 1) {
+      return 0;
+    }
+    p4est_quadrant_parent (&a, &a);
+  }
+  i1 = p4est_quadrant_linear_id (&a, (int) a.level);
+  i2 = p4est_quadrant_linear_id (&b, (int) a.level);
+
+  return (i1 + 1 == i2);
+}
+
+int
+p4est_quadrant_overlaps_tree (p4est_tree_t * tree, const p4est_quadrant_t * q)
+{
+  p4est_quadrant_t    desc;
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  if (tree->quadrants.elem_count == 0)
+    return 0;
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (&tree->first_desc));
+  P4EST_ASSERT (p4est_quadrant_is_valid (&tree->last_desc));
+
+  /* check if the end of q is before the first tree quadrant */
+  p4est_quadrant_last_descendant (q, &desc, P4EST_QMAXLEVEL);
+  if (p4est_quadrant_compare (&desc, &tree->first_desc) < 0)
+    return 0;
+
+  /* check if q is after the last tree quadrant */
+  if (p4est_quadrant_compare (&tree->last_desc, q) < 0)
+    return 0;
+
+  return 1;
+}
+
+int
+p4est_quadrant_is_inside_tree (p4est_tree_t * tree,
+                               const p4est_quadrant_t * q)
+{
+  p4est_quadrant_t    desc;
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  if (tree->quadrants.elem_count == 0)
+    return 0;
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (&tree->first_desc));
+  P4EST_ASSERT (p4est_quadrant_is_valid (&tree->last_desc));
+
+  /* check if q is not before the first tree quadrant */
+  p4est_quadrant_first_descendant (q, &desc, P4EST_QMAXLEVEL);
+  if (p4est_quadrant_compare (&desc, &tree->first_desc) < 0)
+    return 0;
+
+  /* check if the end of q is not after the last tree quadrant */
+  p4est_quadrant_last_descendant (q, &desc, P4EST_QMAXLEVEL);
+  if (p4est_quadrant_compare (&tree->last_desc, q) < 0)
+    return 0;
+
+  return 1;
+}
+
+void
+p4est_quadrant_ancestor (const p4est_quadrant_t * q,
+                         int level, p4est_quadrant_t * r)
+{
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (q->level > level && level >= 0);
+
+  r->x = q->x & ~(P4EST_QUADRANT_LEN (level) - 1);
+  r->y = q->y & ~(P4EST_QUADRANT_LEN (level) - 1);
+#ifdef P4_TO_P8
+  r->z = q->z & ~(P4EST_QUADRANT_LEN (level) - 1);
+#endif
+  r->level = (int8_t) level;
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+}
+
+void
+p4est_quadrant_parent (const p4est_quadrant_t * q, p4est_quadrant_t * r)
+{
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (q->level > 0);
+
+  r->x = q->x & ~P4EST_QUADRANT_LEN (q->level);
+  r->y = q->y & ~P4EST_QUADRANT_LEN (q->level);
+#ifdef P4_TO_P8
+  r->z = q->z & ~P4EST_QUADRANT_LEN (q->level);
+#endif
+  r->level = (int8_t) (q->level - 1);
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+}
+
+void
+p4est_quadrant_sibling (const p4est_quadrant_t * q, p4est_quadrant_t * r,
+                        int sibling_id)
+{
+  const int           addx = (sibling_id & 0x01);
+  const int           addy = (sibling_id & 0x02) >> 1;
+#ifdef P4_TO_P8
+  const int           addz = (sibling_id & 0x04) >> 2;
+#endif
+  const p4est_qcoord_t shift = P4EST_QUADRANT_LEN (q->level);
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (q->level > 0);
+  P4EST_ASSERT (sibling_id >= 0 && sibling_id < P4EST_CHILDREN);
+
+  r->x = (addx ? (q->x | shift) : (q->x & ~shift));
+  r->y = (addy ? (q->y | shift) : (q->y & ~shift));
+#ifdef P4_TO_P8
+  r->z = (addz ? (q->z | shift) : (q->z & ~shift));
+#endif
+  r->level = q->level;
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+}
+
+void
+p4est_quadrant_face_neighbor (const p4est_quadrant_t * q,
+                              int face, p4est_quadrant_t * r)
+{
+  const p4est_qcoord_t qh = P4EST_QUADRANT_LEN (q->level);
+
+  P4EST_ASSERT (0 <= face && face < P4EST_FACES);
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  r->x = q->x + ((face == 0) ? -qh : (face == 1) ? qh : 0);
+  r->y = q->y + ((face == 2) ? -qh : (face == 3) ? qh : 0);
+#ifdef P4_TO_P8
+  r->z = q->z + ((face == 4) ? -qh : (face == 5) ? qh : 0);
+#endif
+  r->level = q->level;
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+}
+
+p4est_topidx_t
+p4est_quadrant_face_neighbor_extra (const p4est_quadrant_t * q,
+                                    p4est_topidx_t t, int face,
+                                    p4est_quadrant_t * r, int *nface,
+                                    p4est_connectivity_t * conn)
+{
+  p4est_quadrant_t    temp;
+  int                 transform[9];
+  p4est_topidx_t      flag;
+
+  p4est_quadrant_face_neighbor (q, face, r);
+  if (p4est_quadrant_is_inside_root (r)) {
+    if (nface != NULL) {
+      *nface = (face ^ 1);
+    }
+    return t;
+  }
+
+  temp = *r;
+  flag = p4est_find_face_transform (conn, t, face, transform);
+  if (flag == -1) {
+    if (r != q) {
+      *r = *q;
+    }
+    if (nface != NULL) {
+      *nface = -1;
+    }
+    return -1;
+  }
+  p4est_quadrant_transform_face (&temp, r, transform);
+  if (nface != NULL) {
+    *nface = (int) conn->tree_to_face[t * P4EST_FACES + face];
+  }
+
+  return flag;
+}
+
+void
+p4est_quadrant_half_face_neighbors (const p4est_quadrant_t * q,
+                                    int face, p4est_quadrant_t n[],
+                                    p4est_quadrant_t nur[])
+{
+  const p4est_qcoord_t qh = P4EST_QUADRANT_LEN (q->level);
+  const p4est_qcoord_t qh_2 = P4EST_QUADRANT_LEN (q->level + 1);
+  int                 i;
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+  P4EST_ASSERT (q->level < P4EST_QMAXLEVEL);
+  P4EST_ASSERT (0 <= face && face < P4EST_FACES);
+
+  n[0].x = q->x + ((face == 0) ? -qh_2 : (face == 1) ? qh : 0);
+  n[0].y = q->y + ((face == 2) ? -qh_2 : (face == 3) ? qh : 0);
+#ifdef P4_TO_P8
+  n[0].z = q->z + ((face == 4) ? -qh_2 : (face == 5) ? qh : 0);
+#endif
+  switch (face / 2) {
+  case 0:
+    for (i = 1; i < P4EST_HALF; ++i) {
+      n[i].x = n[0].x;
+      n[i].y = n[0].y + (i & 0x01) * qh_2;
+#ifdef P4_TO_P8
+      n[i].z = n[0].z + ((i & 0x02) / 2) * qh_2;
+#endif
+    }
+    break;
+  case 1:
+    for (i = 1; i < P4EST_HALF; ++i) {
+      n[i].x = n[0].x + (i & 0x01) * qh_2;
+      n[i].y = n[0].y;
+#ifdef P4_TO_P8
+      n[i].z = n[0].z + ((i & 0x02) / 2) * qh_2;
+#endif
+    }
+    break;
+#ifdef P4_TO_P8
+  case 2:
+    for (i = 1; i < P4EST_HALF; ++i) {
+      n[i].x = n[0].x + (i & 0x01) * qh_2;
+      n[i].y = n[0].y + ((i & 0x02) / 2) * qh_2;
+      n[i].z = n[0].z;
+    }
+    break;
+#endif
+  default:
+    SC_ABORT_NOT_REACHED ();
+    break;
+  }
+  for (i = 0; i < P4EST_HALF; ++i) {
+    n[i].level = (int8_t) (q->level + 1);
+    P4EST_ASSERT (p4est_quadrant_is_extended (&n[i]));
+  }
+
+  if (nur != NULL) {
+    const p4est_qcoord_t dh = qh_2 - P4EST_QUADRANT_LEN (P4EST_QMAXLEVEL);
+
+    for (i = 0; i < P4EST_HALF; ++i) {
+      nur[i].x = n[i].x + dh;
+      nur[i].y = n[i].y + dh;
+#ifdef P4_TO_P8
+      nur[i].z = n[i].z + dh;
+#endif
+      nur[i].level = P4EST_QMAXLEVEL;
+      P4EST_ASSERT (p4est_quadrant_is_extended (&nur[i]));
+    }
+  }
+}
+
+void
+p4est_quadrant_all_face_neighbors (const p4est_quadrant_t * q,
+                                   int face, p4est_quadrant_t n[])
+{
+  const int           qcid = p4est_quadrant_child_id (q);
+  p4est_quadrant_t   *r = &n[P4EST_HALF + 1];
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  if (q->level == P4EST_QMAXLEVEL) {
+    P4EST_QUADRANT_INIT (&n[0]);
+    P4EST_QUADRANT_INIT (&n[1]);
+#ifdef P4_TO_P8
+    P4EST_QUADRANT_INIT (&n[2]);
+    P4EST_QUADRANT_INIT (&n[3]);
+#endif
+  }
+  else {
+    p4est_quadrant_half_face_neighbors (q, face, n, NULL);
+  }
+
+  p4est_quadrant_face_neighbor (q, face, &n[P4EST_HALF]);
+
+  /* Check to see if the larger neighbor exists */
+  if (((qcid >> (face / 2)) & 0x01) != (face & 0x01) || q->level == 0) {
+    P4EST_QUADRANT_INIT (r);
+  }
+  else {
+    p4est_quadrant_parent (q, r);
+    p4est_quadrant_face_neighbor (r, face, r);
+  }
+}
+
+void
+p4est_quadrant_corner_neighbor (const p4est_quadrant_t * q,
+                                int corner, p4est_quadrant_t * r)
+{
+  const p4est_qcoord_t qh = P4EST_QUADRANT_LEN (q->level);
+
+  P4EST_ASSERT (0 <= corner && corner < P4EST_CHILDREN);
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  r->x = q->x + (2 * (corner & 0x01) - 1) * qh;
+  r->y = q->y + ((corner & 0x02) - 1) * qh;
+#ifdef P4_TO_P8
+  r->z = q->z + ((corner & 0x04) / 2 - 1) * qh;
+#endif
+  r->level = q->level;
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+}
+
+void
+p4est_quadrant_corner_neighbor_extra (const p4est_quadrant_t * q,
+                                      p4est_locidx_t t, int corner,
+                                      sc_array_t * quads,
+                                      sc_array_t * treeids,
+                                      sc_array_t * ncorners,
+                                      p4est_connectivity_t * conn)
+{
+  p4est_quadrant_t    temp;
+  p4est_quadrant_t   *qp;
+  p4est_topidx_t     *tp;
+  int                 face;
+  int                *ip;
+  size_t              ctree;
+  p4est_corner_info_t ci;
+  p4est_corner_transform_t *ct;
+  sc_array_t         *cta = &ci.corner_transforms;
+#ifdef P4_TO_P8
+  int                 edge;
+  int                 i;
+#endif
+
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (quads));
+  P4EST_ASSERT (quads->elem_count == 0);
+  P4EST_ASSERT (quads->elem_size == sizeof (p4est_quadrant_t));
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (treeids));
+  P4EST_ASSERT (treeids->elem_count == 0);
+  P4EST_ASSERT (treeids->elem_size == sizeof (p4est_topidx_t));
+  if (ncorners != NULL) {
+    P4EST_ASSERT (SC_ARRAY_IS_OWNER (ncorners));
+    P4EST_ASSERT (ncorners->elem_count == 0);
+    P4EST_ASSERT (ncorners->elem_size == sizeof (int));
+  }
+
+  p4est_quadrant_corner_neighbor (q, corner, &temp);
+  if (p4est_quadrant_is_inside_root (&temp)) {
+    qp = p4est_quadrant_array_push (quads);
+    *qp = temp;
+    tp = (p4est_topidx_t *) sc_array_push (treeids);
+    *tp = t;
+    if (ncorners != NULL) {
+      ip = (int *) sc_array_push (ncorners);
+      *ip = (corner ^ (P4EST_CHILDREN - 1));
+    }
+    return;
+  }
+
+  if (!p4est_quadrant_is_outside_corner (&temp)) {
+#ifndef P4_TO_P8
+    qp = (p4est_quadrant_t *) sc_array_push (quads);
+    tp = (p4est_topidx_t *) sc_array_push (treeids);
+
+    face = p4est_corner_faces[corner][0];
+    p4est_quadrant_face_neighbor (q, face, &temp);
+    if (p4est_quadrant_is_inside_root (&temp)) {
+      face = p4est_corner_faces[corner][1];
+      *tp = p4est_quadrant_face_neighbor_extra (&temp, t, face, qp, NULL,
+                                                conn);
+      if (*tp == -1) {
+        qp = (p4est_quadrant_t *) sc_array_pop (quads);
+        tp = (p4est_topidx_t *) sc_array_pop (treeids);
+      }
+      else if (ncorners != NULL) {
+        int                 opc = (corner ^ 1);
+        int                 nface =
+          conn->tree_to_face[P4EST_FACES * t + face];
+        int                 o = nface / P4EST_FACES;
+        int                 nc;
+        int                 c;
+
+        nface = nface % P4EST_FACES;
+        nc = p4est_corner_face_corners[opc][face];
+
+        P4EST_ASSERT (nc >= 0);
+        c = (o ? (nc ^ 1) : nc);
+        nc = p4est_face_corners[nface][c];
+
+        ip = (int *) sc_array_push (ncorners);
+        *ip = nc;
+      }
+      return;
+    }
+    face = p4est_corner_faces[corner][1];
+    p4est_quadrant_face_neighbor (q, face, &temp);
+    P4EST_ASSERT (p4est_quadrant_is_inside_root (&temp));
+
+    face = p4est_corner_faces[corner][0];
+    *tp = p4est_quadrant_face_neighbor_extra (&temp, t, face, qp, NULL, conn);
+    if (*tp == -1) {
+      qp = (p4est_quadrant_t *) sc_array_pop (quads);
+      tp = (p4est_topidx_t *) sc_array_pop (treeids);
+    }
+    else if (ncorners != NULL) {
+      int                 opc = (corner ^ 2);
+      int                 nface = conn->tree_to_face[P4EST_FACES * t + face];
+      int                 o = nface / P4EST_FACES;
+      int                 nc;
+      int                 c;
+
+      nface = nface % P4EST_FACES;
+      nc = p4est_corner_face_corners[opc][face];
+
+      P4EST_ASSERT (nc >= 0);
+      c = (o ? (nc ^ 1) : nc);
+      nc = p4est_face_corners[nface][c];
+
+      ip = (int *) sc_array_push (ncorners);
+      *ip = nc;
+    }
+    return;
+#else
+    for (i = 0; i < 3; i++) {
+      face = p8est_corner_faces[corner][i];
+      edge = p8est_corner_edges[corner][i];
+      p4est_quadrant_face_neighbor (q, face, &temp);
+      if (p4est_quadrant_is_inside_root (&temp)) {
+        p8est_quadrant_edge_neighbor_extra (&temp, t, edge, quads, treeids,
+                                            ncorners, conn);
+
+        if (ncorners != NULL) {
+          size_t              zz;
+          size_t              ce =
+            (p8est_edge_corners[edge][0] == corner ? 0 : 1);
+          int                 nedge;
+          int                 o;
+
+          for (zz = 0; zz < ncorners->elem_count; zz++) {
+            ip = (int *) sc_array_index (ncorners, zz);
+            nedge = *ip;
+            o = nedge / P8EST_EDGES;
+            nedge = nedge % P8EST_EDGES;
+            *ip = p8est_edge_corners[nedge][o ? ce : (ce ^ 1)];
+          }
+        }
+        return;
+      }
+    }
+    SC_ABORT_NOT_REACHED ();
+#endif
+  }
+  sc_array_init (cta, sizeof (p4est_corner_transform_t));
+  p4est_find_corner_transform (conn, t, corner, &ci);
+  sc_array_resize (quads, cta->elem_count);
+  sc_array_resize (treeids, cta->elem_count);
+  if (ncorners != NULL) {
+    sc_array_resize (ncorners, cta->elem_count);
+  }
+  for (ctree = 0; ctree < cta->elem_count; ++ctree) {
+    qp = p4est_quadrant_array_index (quads, ctree);
+    tp = (p4est_topidx_t *) sc_array_index (treeids, ctree);
+    ct = p4est_corner_array_index (cta, ctree);
+    p4est_quadrant_transform_corner (&temp, (int) ct->ncorner, 1);
+    *qp = temp;
+    *tp = ct->ntree;
+    if (ncorners != NULL) {
+      ip = (int *) sc_array_index (ncorners, ctree);
+      *ip = ct->ncorner;
+    }
+  }
+  sc_array_reset (cta);
+}
+
+void
+p4est_quadrant_half_corner_neighbor (const p4est_quadrant_t * q, int corner,
+                                     p4est_quadrant_t * r)
+{
+  const p4est_qcoord_t qh = P4EST_QUADRANT_LEN (q->level);
+  const p4est_qcoord_t mqh_2 = -P4EST_QUADRANT_LEN (q->level + 1);
+
+  P4EST_ASSERT (0 <= corner && corner < P4EST_CHILDREN);
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+  P4EST_ASSERT (q->level < P4EST_QMAXLEVEL);
+
+  r->x = q->x + ((corner & 0x01) ? qh : mqh_2);
+  r->y = q->y + ((corner & 0x02) ? qh : mqh_2);
+#ifdef P4_TO_P8
+  r->z = q->z + ((corner & 0x04) ? qh : mqh_2);
+#endif
+  r->level = (int8_t) (q->level + 1);
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+}
+
+void
+p4est_quadrant_corner_node (const p4est_quadrant_t * q,
+                            int corner, p4est_quadrant_t * r)
+{
+  const p4est_qcoord_t qh = P4EST_QUADRANT_LEN (q->level);
+
+  P4EST_ASSERT (0 <= corner && corner < P4EST_CHILDREN);
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  r->x = q->x + (corner & 0x01) * qh;
+  r->y = q->y + ((corner & 0x02) >> 1) * qh;
+#ifdef P4_TO_P8
+  r->z = q->z + ((corner & 0x04) >> 2) * qh;
+#endif
+  r->level = P4EST_MAXLEVEL;
+  P4EST_ASSERT (p4est_quadrant_is_node (r, 0));
+}
+
+void
+p4est_quadrant_children (const p4est_quadrant_t * q,
+                         p4est_quadrant_t * c0, p4est_quadrant_t * c1,
+                         p4est_quadrant_t * c2, p4est_quadrant_t * c3
+#ifdef P4_TO_P8
+                         , p4est_quadrant_t * c4, p4est_quadrant_t * c5,
+                         p4est_quadrant_t * c6, p4est_quadrant_t * c7
+#endif
+  )
+{
+  const int8_t        level = (int8_t) (q->level + 1);
+  const p4est_qcoord_t inc = P4EST_QUADRANT_LEN (level);
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT (q->level < P4EST_QMAXLEVEL);
+
+  c0->x = q->x;
+  c0->y = q->y;
+#ifdef P4_TO_P8
+  c0->z = q->z;
+#endif
+  c0->level = level;
+
+  c1->x = c0->x | inc;
+  c1->y = c0->y;
+#ifdef P4_TO_P8
+  c1->z = c0->z;
+#endif
+  c1->level = level;
+
+  c2->x = c0->x;
+  c2->y = c0->y | inc;
+#ifdef P4_TO_P8
+  c2->z = c0->z;
+#endif
+  c2->level = level;
+
+  c3->x = c1->x;
+  c3->y = c2->y;
+#ifdef P4_TO_P8
+  c3->z = c0->z;
+#endif
+  c3->level = level;
+
+#ifdef P4_TO_P8
+  c4->x = c0->x;
+  c4->y = c0->y;
+  c4->z = c0->z | inc;
+  c4->level = level;
+
+  c5->x = c1->x;
+  c5->y = c1->y;
+  c5->z = c4->z;
+  c5->level = level;
+
+  c6->x = c2->x;
+  c6->y = c2->y;
+  c6->z = c4->z;
+  c6->level = level;
+
+  c7->x = c3->x;
+  c7->y = c3->y;
+  c7->z = c4->z;
+  c7->level = level;
+#endif
+
+  /* this also verifies p4est_quadrant_is_extended (c[i]) */
+#ifndef P4_TO_P8
+  P4EST_ASSERT (p4est_quadrant_is_family (c0, c1, c2, c3));
+#else
+  P4EST_ASSERT (p4est_quadrant_is_family (c0, c1, c2, c3, c4, c5, c6, c7));
+#endif
+}
+
+void
+p4est_quadrant_childrenv (const p4est_quadrant_t * q, p4est_quadrant_t c[])
+{
+  p4est_quadrant_children (q, &c[0], &c[1], &c[2], &c[3]
+#ifdef P4_TO_P8
+                           , &c[4], &c[5], &c[6], &c[7]
+#endif
+    );
+}
+
+void
+p4est_quadrant_childrenpv (const p4est_quadrant_t * q, p4est_quadrant_t * c[])
+{
+  p4est_quadrant_children (q, c[0], c[1], c[2], c[3]
+#ifdef P4_TO_P8
+                           , c[4], c[5], c[6], c[7]
+#endif
+    );
+}
+
+void
+p4est_quadrant_first_descendant (const p4est_quadrant_t * q,
+                                 p4est_quadrant_t * fd, int level)
+{
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT ((int) q->level <= level && level <= P4EST_QMAXLEVEL);
+
+  fd->x = q->x;
+  fd->y = q->y;
+#ifdef P4_TO_P8
+  fd->z = q->z;
+#endif
+  fd->level = (int8_t) level;
+}
+
+void
+p4est_quadrant_last_descendant (const p4est_quadrant_t * q,
+                                p4est_quadrant_t * ld, int level)
+{
+  p4est_qcoord_t      shift;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q));
+  P4EST_ASSERT ((int) q->level <= level && level <= P4EST_QMAXLEVEL);
+
+  shift = P4EST_QUADRANT_LEN (q->level) - P4EST_QUADRANT_LEN (level);
+
+  ld->x = q->x + shift;
+  ld->y = q->y + shift;
+#ifdef P4_TO_P8
+  ld->z = q->z + shift;
+#endif
+  ld->level = (int8_t) level;
+}
+
+void
+p4est_quadrant_corner_descendant (const p4est_quadrant_t * q,
+                                  p4est_quadrant_t * r, int c, int level)
+{
+  p4est_qcoord_t      shift = P4EST_QUADRANT_LEN (q->level) -
+    P4EST_QUADRANT_LEN (level);
+  P4EST_ASSERT (level >= (int) q->level && level <= P4EST_QMAXLEVEL);
+  r->x = q->x + ((c & 1) ? shift : 0);
+  r->y = q->y + (((c >> 1) & 1) ? shift : 0);
+#ifdef P4_TO_P8
+  r->z = q->z + ((c >> 2) ? shift : 0);
+#endif
+  r->level = (int8_t) level;
+}
+
+void
+p4est_nearest_common_ancestor (const p4est_quadrant_t * q1,
+                               const p4est_quadrant_t * q2,
+                               p4est_quadrant_t * r)
+{
+  int                 maxlevel;
+  uint32_t            exclorx, exclory;
+#ifdef P4_TO_P8
+  uint32_t            exclorz;
+#endif
+  uint32_t            maxclor;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q1));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q2));
+
+  exclorx = q1->x ^ q2->x;
+  exclory = q1->y ^ q2->y;
+#ifdef P4_TO_P8
+  exclorz = q1->z ^ q2->z;
+
+  maxclor = exclorx | exclory | exclorz;
+#else
+  maxclor = exclorx | exclory;
+#endif
+  maxlevel = SC_LOG2_32 (maxclor) + 1;
+
+  P4EST_ASSERT (maxlevel <= P4EST_MAXLEVEL);
+
+  r->x = q1->x & ~((1 << maxlevel) - 1);
+  r->y = q1->y & ~((1 << maxlevel) - 1);
+#ifdef P4_TO_P8
+  r->z = q1->z & ~((1 << maxlevel) - 1);
+#endif
+  r->level = (int8_t) SC_MIN (P4EST_MAXLEVEL - maxlevel,
+                              (int) SC_MIN (q1->level, q2->level));
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+}
+
+void
+p4est_nearest_common_ancestor_D (const p4est_quadrant_t * q1,
+                                 const p4est_quadrant_t * q2,
+                                 p4est_quadrant_t * r)
+{
+  p4est_quadrant_t    s1 = *q1;
+  p4est_quadrant_t    s2 = *q2;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q1));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q2));
+
+  /* first stage: promote the deepest one to the same level */
+  while (s1.level > s2.level) {
+    p4est_quadrant_parent (&s1, &s1);
+  }
+  while (s1.level < s2.level) {
+    p4est_quadrant_parent (&s2, &s2);
+  }
+  P4EST_ASSERT (s1.level == s2.level);
+
+  /* second stage: simultaneously go through their parents */
+  while (!p4est_quadrant_is_equal (&s1, &s2)) {
+    p4est_quadrant_parent (&s1, &s1);
+    p4est_quadrant_parent (&s2, &s2);
+  }
+
+  /* don't overwrite r's user_data */
+  r->x = s1.x;
+  r->y = s1.y;
+#ifdef P4_TO_P8
+  r->z = s1.z;
+#endif
+  r->level = s1.level;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+}
+
+void
+p4est_quadrant_transform_face (const p4est_quadrant_t * q,
+                               p4est_quadrant_t * r, const int ftransform[])
+{
+  p4est_qcoord_t      mh, tRmh, Rmh;
+  p4est_qcoord_t     *target_xyz[P4EST_DIM];
+  const p4est_qcoord_t *my_xyz[P4EST_DIM];
+  const int          *my_axis = &ftransform[0];
+  const int          *target_axis = &ftransform[3];
+  const int          *edge_reverse = &ftransform[6];
+
+#ifdef P4EST_ENABLE_DEBUG
+  int                 i;
+
+  for (i = 0; i < 3; ++i) {
+    P4EST_ASSERT (0 <= my_axis[i] && my_axis[i] < P4EST_DIM);
+    P4EST_ASSERT (0 <= target_axis[i] && target_axis[i] < P4EST_DIM);
+  }
+#endif
+
+  P4EST_ASSERT (my_axis[0] != my_axis[2]);
+  P4EST_ASSERT (target_axis[0] != target_axis[2]);
+  P4EST_ASSERT (0 <= edge_reverse[0] && edge_reverse[0] < 2);
+  P4EST_ASSERT (0 <= edge_reverse[2] && edge_reverse[2] < 4);
+#ifdef P4_TO_P8
+  P4EST_ASSERT (my_axis[0] != my_axis[1] && my_axis[1] != my_axis[2]);
+  P4EST_ASSERT (target_axis[0] != target_axis[1] &&
+                target_axis[1] != target_axis[2]);
+  P4EST_ASSERT (0 <= edge_reverse[1] && edge_reverse[1] < 2);
+#else
+  P4EST_ASSERT (my_axis[1] == 0 && target_axis[1] == 0);
+  P4EST_ASSERT (edge_reverse[1] == 0);
+#endif
+  P4EST_ASSERT (q != r);
+
+  if (q->level == P4EST_MAXLEVEL) {
+    P4EST_ASSERT (p4est_quadrant_is_node (q, 0));
+    mh = 0;
+    /* if (P4EST_MAXLEVEL == 30) tRmh will overflow to INT32_MIN < 0 */
+  }
+  else {
+    P4EST_ASSERT (p4est_quadrant_is_extended (q));
+    mh = -P4EST_QUADRANT_LEN (q->level);
+  }
+  Rmh = P4EST_ROOT_LEN + mh;
+  tRmh = P4EST_ROOT_LEN + Rmh;
+
+  my_xyz[0] = &q->x;
+  my_xyz[1] = &q->y;
+#ifdef P4_TO_P8
+  my_xyz[2] = &q->z;
+#endif
+
+  target_xyz[0] = &r->x;
+  target_xyz[1] = &r->y;
+#ifdef P4_TO_P8
+  target_xyz[2] = &r->z;
+#endif
+
+#ifdef P4EST_ENABLE_DEBUG
+  r->x = r->y = (p4est_qcoord_t) P4EST_QCOORD_MIN;
+#ifdef P4_TO_P8
+  r->z = (p4est_qcoord_t) P4EST_QCOORD_MIN;
+#endif
+#endif
+
+  *target_xyz[target_axis[0]] =
+    !edge_reverse[0] ? *my_xyz[my_axis[0]] : Rmh - *my_xyz[my_axis[0]];
+#ifdef P4_TO_P8
+  *target_xyz[target_axis[1]] =
+    !edge_reverse[1] ? *my_xyz[my_axis[1]] : Rmh - *my_xyz[my_axis[1]];
+#endif
+  switch (edge_reverse[2]) {
+  case 0:
+    *target_xyz[target_axis[2]] = mh - *my_xyz[my_axis[2]];
+    break;
+  case 1:
+    *target_xyz[target_axis[2]] = *my_xyz[my_axis[2]] + P4EST_ROOT_LEN;
+    break;
+  case 2:
+    *target_xyz[target_axis[2]] = *my_xyz[my_axis[2]] - P4EST_ROOT_LEN;
+    break;
+  case 3:
+    *target_xyz[target_axis[2]] = tRmh - *my_xyz[my_axis[2]];
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+
+#ifdef P4EST_ENABLE_DEBUG
+  {
+    /* This is the code from the paper -- not sure which is preferable. */
+
+    int                 d_iface_and1, d_tface_and1;
+    int                 sprime;
+    p4est_qcoord_t      qcoord;
+
+    d_iface_and1 = ftransform[8] >> 1;
+    d_tface_and1 = ftransform[8] & 1;
+    sprime = 1 - (d_iface_and1 ^ d_tface_and1);
+
+    qcoord = P4EST_ROOT_LEN * (2 * d_tface_and1 - 1)
+      + sprime * Rmh + (1 - 2 * sprime) * *my_xyz[my_axis[2]];
+
+    P4EST_ASSERT (qcoord == *target_xyz[target_axis[2]]);
+  }
+#endif
+
+  r->level = q->level;
+#ifdef P4EST_ENABLE_DEBUG
+  if (r->level == P4EST_MAXLEVEL) {
+    P4EST_ASSERT (p4est_quadrant_is_node (r, 0));
+  }
+  else {
+    P4EST_ASSERT (p4est_quadrant_is_extended (r));
+    P4EST_ASSERT ((p4est_quadrant_is_inside_root (q) &&
+                   !p4est_quadrant_is_inside_root (r)) ||
+                  (!p4est_quadrant_is_inside_root (q) &&
+                   p4est_quadrant_is_inside_root (r)));
+  }
+#endif
+}
+
+int
+p4est_quadrant_touches_corner (const p4est_quadrant_t * q,
+                               int corner, int inside)
+{
+  int                 quad_contact[P4EST_FACES];
+  int                 side, incount;
+  p4est_qcoord_t      lower, upper;
+
+  P4EST_ASSERT (0 <= corner && corner < P4EST_CHILDREN);
+
+  if (q->level == P4EST_MAXLEVEL) {
+    P4EST_ASSERT (p4est_quadrant_is_node (q, inside));
+    lower = 0;
+    upper = P4EST_ROOT_LEN - (inside ? 1 : 0);
+  }
+  else {
+    if (!inside) {
+      P4EST_ASSERT (p4est_quadrant_is_extended (q));
+      lower = -P4EST_QUADRANT_LEN (q->level);
+      upper = P4EST_ROOT_LEN;
+    }
+    else {
+      P4EST_ASSERT (p4est_quadrant_is_valid (q));
+      lower = 0;
+      upper = P4EST_LAST_OFFSET (q->level);
+    }
+  }
+
+  quad_contact[0] = (q->x == lower);
+  quad_contact[1] = (q->x == upper);
+  quad_contact[2] = (q->y == lower);
+  quad_contact[3] = (q->y == upper);
+#ifdef P4_TO_P8
+  quad_contact[4] = (q->z == lower);
+  quad_contact[5] = (q->z == upper);
+#endif
+
+  incount = 0;
+  side = (corner & 1);
+  incount += quad_contact[side];
+  side = (corner >> 1) & 1;
+  incount += quad_contact[2 + side];
+#ifdef P4_TO_P8
+  side = (corner >> 2);
+  incount += quad_contact[4 + side];
+#endif
+
+  return incount == P4EST_DIM;
+}
+
+void
+p4est_quadrant_transform_corner (p4est_quadrant_t * q, int corner, int inside)
+{
+  p4est_qcoord_t      shift[2];
+
+  P4EST_ASSERT (0 <= corner && corner < P4EST_CHILDREN);
+  if (q->level == P4EST_MAXLEVEL) {
+    P4EST_ASSERT (!inside);
+    shift[0] = 0;
+    shift[1] = P4EST_ROOT_LEN;
+  }
+  else {
+    P4EST_ASSERT (0 <= q->level && q->level <= P4EST_QMAXLEVEL);
+    shift[0] = (inside ? 0 : -P4EST_QUADRANT_LEN (q->level));
+    shift[1] = (inside ? P4EST_LAST_OFFSET (q->level) : P4EST_ROOT_LEN);
+  }
+
+  q->x = shift[corner & 1];
+  q->y = shift[(corner >> 1) & 1];
+#ifdef P4_TO_P8
+  q->z = shift[corner >> 2];
+#endif
+
+  P4EST_ASSERT (p4est_quadrant_touches_corner (q, corner, inside));
+}
+
+void
+p4est_quadrant_shift_corner (const p4est_quadrant_t * q,
+                             p4est_quadrant_t * r, int corner)
+{
+  int                 outface;
+  int                 step[P4EST_DIM];
+  p4est_qcoord_t      th;
+  p4est_quadrant_t    quad;
+  /* *INDENT-OFF* */
+  const int           contact[P4EST_CHILDREN] = {
+#ifndef P4_TO_P8
+    0x05, 0x06, 0x09, 0x0a,
+#else
+    0x15, 0x16, 0x19, 0x1a,
+    0x25, 0x26, 0x29, 0x2a,
+#endif
+  };
+  /* *INDENT-ON* */
+
+  P4EST_ASSERT (q != r);
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+  P4EST_ASSERT (corner >= 0 && corner < P4EST_CHILDREN);
+
+  P4EST_QUADRANT_INIT (&quad);
+
+  quad = *q;
+  for (;;) {
+    th = P4EST_LAST_OFFSET (quad.level);
+    step[0] = 2 * (corner & 0x01) - 1;
+    step[1] = (corner & 0x02) - 1;
+#ifdef P4_TO_P8
+    step[2] = (corner & 0x04) / 2 - 1;
+#endif
+    p4est_quadrant_sibling (&quad, r, corner);
+    P4EST_ASSERT (-1 == step[0] || step[0] == 1);
+    P4EST_ASSERT (-1 == step[1] || step[1] == 1);
+#ifdef P4_TO_P8
+    P4EST_ASSERT (-1 == step[2] || step[2] == 1);
+#endif
+
+    outface = 0;
+    outface |= ((r->x <= 0) ? 0x01 : 0);
+    outface |= ((r->x >= th) ? 0x02 : 0);
+    outface |= ((r->y <= 0) ? 0x04 : 0);
+    outface |= ((r->y >= th) ? 0x08 : 0);
+#ifdef P4_TO_P8
+    outface |= ((r->z <= 0) ? 0x10 : 0);
+    outface |= ((r->z >= th) ? 0x20 : 0);
+#endif
+
+    if (outface == contact[corner]) {
+      break;
+    }
+    p4est_quadrant_parent (&quad, &quad);
+    quad.x += (p4est_qcoord_t) step[0] * P4EST_QUADRANT_LEN (quad.level);
+    quad.y += (p4est_qcoord_t) step[1] * P4EST_QUADRANT_LEN (quad.level);
+#ifdef P4_TO_P8
+    quad.z += (p4est_qcoord_t) step[2] * P4EST_QUADRANT_LEN (quad.level);
+#endif
+    P4EST_ASSERT (p4est_quadrant_is_extended (&quad));
+  }
+
+  if (r->x < 0)
+    r->x = 0;
+  if (r->x >= P4EST_ROOT_LEN)
+    r->x = th;
+  if (r->y < 0)
+    r->y = 0;
+  if (r->y >= P4EST_ROOT_LEN)
+    r->y = th;
+#ifdef P4_TO_P8
+  if (r->z < 0)
+    r->z = 0;
+  if (r->z >= P4EST_ROOT_LEN)
+    r->z = th;
+#endif
+
+  P4EST_ASSERT (p4est_quadrant_touches_corner (r, corner, 1));
+}
+
+uint64_t
+p4est_quadrant_linear_id (const p4est_quadrant_t * quadrant, int level)
+{
+  int                 i;
+  uint64_t            id;
+  uint64_t            x, y;
+#ifdef P4_TO_P8
+  uint64_t            z;
+#endif
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (quadrant));
+  P4EST_ASSERT ((int) quadrant->level >= level && level >= 0);
+
+  /* this preserves the high bits from negative numbers */
+  x = quadrant->x >> (P4EST_MAXLEVEL - level);
+  y = quadrant->y >> (P4EST_MAXLEVEL - level);
+#ifdef P4_TO_P8
+  z = quadrant->z >> (P4EST_MAXLEVEL - level);
+#endif
+
+  id = 0;
+  for (i = 0; i < level + 2; ++i) {
+    id |= ((x & ((uint64_t) 1 << i)) << ((P4EST_DIM - 1) * i));
+    id |= ((y & ((uint64_t) 1 << i)) << ((P4EST_DIM - 1) * i + 1));
+#ifdef P4_TO_P8
+    id |= ((z & ((uint64_t) 1 << i)) << ((P4EST_DIM - 1) * i + 2));
+#endif
+  }
+
+  return id;
+}
+
+void
+p4est_quadrant_set_morton (p4est_quadrant_t * quadrant,
+                           int level, uint64_t id)
+{
+  int                 i;
+
+  P4EST_ASSERT (0 <= level && level <= P4EST_QMAXLEVEL);
+  if (level < P4EST_QMAXLEVEL) {
+    P4EST_ASSERT (id < ((uint64_t) 1 << P4EST_DIM * (level + 2)));
+  }
+
+  quadrant->level = (int8_t) level;
+  quadrant->x = 0;
+  quadrant->y = 0;
+#ifdef P4_TO_P8
+  quadrant->z = 0;
+#endif
+
+  /* this may set the sign bit to create negative numbers */
+  for (i = 0; i < level + 2; ++i) {
+    quadrant->x |= (p4est_qcoord_t) ((id & (1ULL << (P4EST_DIM * i)))
+                                     >> ((P4EST_DIM - 1) * i));
+    quadrant->y |= (p4est_qcoord_t) ((id & (1ULL << (P4EST_DIM * i + 1)))
+                                     >> ((P4EST_DIM - 1) * i + 1));
+#ifdef P4_TO_P8
+    quadrant->z |= (p4est_qcoord_t) ((id & (1ULL << (P4EST_DIM * i + 2)))
+                                     >> ((P4EST_DIM - 1) * i + 2));
+#endif
+  }
+
+  quadrant->x <<= (P4EST_MAXLEVEL - level);
+  quadrant->y <<= (P4EST_MAXLEVEL - level);
+#ifdef P4_TO_P8
+  quadrant->z <<= (P4EST_MAXLEVEL - level);
+
+  /* this is needed whenever the number of bits is more than MAXLEVEL + 2 */
+  if (quadrant->x >= (p4est_qcoord_t) 1 << (P4EST_MAXLEVEL + 1))
+    quadrant->x -= (p4est_qcoord_t) 1 << (P4EST_MAXLEVEL + 2);
+  if (quadrant->y >= (p4est_qcoord_t) 1 << (P4EST_MAXLEVEL + 1))
+    quadrant->y -= (p4est_qcoord_t) 1 << (P4EST_MAXLEVEL + 2);
+  if (quadrant->z >= (p4est_qcoord_t) 1 << (P4EST_MAXLEVEL + 1))
+    quadrant->z -= (p4est_qcoord_t) 1 << (P4EST_MAXLEVEL + 2);
+#endif
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (quadrant));
+}
diff --git a/src/p4est_bits.h b/src/p4est_bits.h
new file mode 100644
index 0000000..f84ce7a
--- /dev/null
+++ b/src/p4est_bits.h
@@ -0,0 +1,604 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_bits.h
+ *
+ * Routines for manipulating quadrants (neighbors, parents, children, etc.)
+ *
+ * \ingroup p4est
+ */
+
+#ifndef P4EST_BITS_H
+#define P4EST_BITS_H
+
+#include <p4est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Prints one line with quadrant's x, y and level.
+ * \param [in] log_priority  see \ref logpriorities in sc.h for the meanings
+ *                           of numerical priority values
+ * \param [in] q             quadrant to print
+ */
+void                p4est_quadrant_print (int log_priority,
+                                          const p4est_quadrant_t * q);
+
+/** Test if two quadrants have equal Morton indices.
+ * \return true if \a q1 describes the same quadrant as \a q2.
+ */
+int                 p4est_quadrant_is_equal (const p4est_quadrant_t * q1,
+                                             const p4est_quadrant_t * q2);
+
+/** Test if two quadrants overlap.
+ * \return true if \a q1 and \a q2 are equal or one is the ancestor of the
+ * other.
+ */
+int                 p4est_quadrant_overlaps (const p4est_quadrant_t * q1,
+                                             const p4est_quadrant_t * q2);
+
+/** Test if two quadrants have equal Morton indices and the same tree id.
+ * \return          true if \a q1 describes the same quadrant as \a q2
+ *                  and the p.which_tree fields are equal.
+ */
+int                 p4est_quadrant_is_equal_piggy (const p4est_quadrant_t *
+                                                   q1,
+                                                   const p4est_quadrant_t *
+                                                   q2);
+
+/** Compare two quadrants in their Morton ordering.
+ * Both quadrants must be valid.
+ * \return Returns < 0 if \a v1 < \a v2,
+ *                   0 if \a v1 == \a v2,
+ *                 > 0 if \a v1 > \a v2
+ */
+int                 p4est_quadrant_compare (const void *v1, const void *v2);
+
+/** Compare two quadrants in their Morton ordering, with equivalence if the
+ * two quadrants overlap.
+ * \return Returns < 0 if \a v1 < \a v2 and \a v1 and \a v2 do not overlap,
+ *                   0 if \a v1 and \a v2 overlap,
+ *                 > 0 if \a v1 > \a v2 and \a v1 and \a v2 do not overlap.
+ */
+int                 p4est_quadrant_disjoint (const void *v1, const void *v2);
+
+/** Compare two quadrants in their Morton ordering and the which_tree member.
+ * Both quadrants must be extended (superset of valid, see below).
+ * \return Returns < 0 if \a v1 < \a v2,
+ *                   0 if \a v1 == \a v2,
+ *                 > 0 if \a v1 > \a v2
+ */
+int                 p4est_quadrant_compare_piggy (const void *v1,
+                                                  const void *v2);
+
+/** Compare two quadrants with respect to their local_num in the piggy3 member.
+ * \return Returns < 0 if \a v1 < \a v2,
+ *                   0 if \a v1 == \a v2,
+ *                 > 0 if \a v1 > \a v2
+ */
+int                 p4est_quadrant_compare_local_num (const void *v1,
+                                                      const void *v2);
+
+/** Test if two quadrants have equal Morton indices, callback version.
+ * \return true if \a v1 describes the same quadrant as \a v2.
+ */
+int                 p4est_quadrant_equal_fn (const void *v1, const void *v2,
+                                             const void *u);
+
+/** Computes a hash value for a quadrant by the lookup3 method.
+ */
+unsigned            p4est_quadrant_hash_fn (const void *v, const void *u);
+
+/** Test if two nodes are in the same tree and have equal Morton indices.
+ * \param [in] v1   Pointer to a clamped or unclamped node, depending on u.
+ * \param [in] v2   Pointer to a clamped or unclamped node, depending on u.
+ * \param [in] u    User data, points to an int holding the clamped-flag.
+ */
+int                 p4est_node_equal_piggy_fn (const void *v1,
+                                               const void *v2, const void *u);
+
+/** Compute hash value of a node based on its tree and Morton index.
+ * \param [in] v    Pointer to a clamped or unclamped node, depending on u.
+ * \param [in] u    User data, points to an int holding the clamped-flag.
+ */
+unsigned            p4est_node_hash_piggy_fn (const void *v, const void *u);
+
+/** Clamp a node inside the unit tree if it sits on a high border.
+ * \param [in] n    Node to be clamped. Must not yet be clamped.
+ * \param [out] r   Existing node overwritten by the clamped result.
+ */
+void                p4est_node_clamp_inside (const p4est_quadrant_t * n,
+                                             p4est_quadrant_t * r);
+
+/** Move a clamped node out on the border.
+ * \param [in] n    Node to be unclamped in-place.
+ */
+void                p4est_node_unclamp (p4est_quadrant_t * n);
+
+/** Find the enclosing quadrant of a given node at a given level.
+ * \param [in] n        Clamped node.
+ * \param [in] level    Level of the quadrant to be created.
+ * \param [out] q       Output quadrant, n == q is permitted.
+ */
+void                p4est_node_to_quadrant (const p4est_quadrant_t * n,
+                                            int level, p4est_quadrant_t * q);
+
+/** Decide if a node is completely contained within a quadrant.
+ * \param [in] q        Valid quadrant.
+ * \param [in] n        Clamped node.
+ */
+int                 p4est_quadrant_contains_node (const p4est_quadrant_t * q,
+                                                  const p4est_quadrant_t * n);
+
+/** Compute the position of the ancestor of this child at level \a level within
+ * its siblings.
+ * \return Returns its child id in 0..3
+ */
+int                 p4est_quadrant_ancestor_id (const p4est_quadrant_t * q,
+                                                int level);
+
+/** Compute the position of this child within its siblings.
+ * \return Returns its child id in 0..3
+ */
+int                 p4est_quadrant_child_id (const p4est_quadrant_t * q);
+
+/** Test if a quadrant is inside the unit tree.
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is inside the unit tree.
+ */
+int                 p4est_quadrant_is_inside_root (const p4est_quadrant_t *
+                                                   q);
+
+/** Test if a quadrant is inside the 3x3 box around the root tree.
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is inside the unit tree.
+ */
+int                 p4est_quadrant_is_inside_3x3 (const p4est_quadrant_t * q);
+
+/** Test if a quadrant is outside a tree face boundary (no corner).
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is outside across a unit tree face.
+ */
+int                 p4est_quadrant_is_outside_face (const p4est_quadrant_t *
+                                                    q);
+
+/** Test if a quadrant is outside a tree corner boundary.
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is outside across a unit tree corner.
+ */
+int                 p4est_quadrant_is_outside_corner (const p4est_quadrant_t *
+                                                      q);
+
+/** Test if a quadrant is used to represent a mesh node.
+ * \param [in] q        Quadrant to be tested.
+ * \param [in] inside   If true, boundary nodes must be clamped inside.
+ *                      If false, nodes must align with the quadrant grid.
+ * \return Returns true if \a q is a node.
+ */
+int                 p4est_quadrant_is_node (const p4est_quadrant_t * q,
+                                            int inside);
+
+/** Test if a quadrant has valid Morton indices and is inside the unit tree.
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is valid.
+ */
+int                 p4est_quadrant_is_valid (const p4est_quadrant_t * q);
+
+/** Test if a quadrant has valid Morton indices in the 3x3 box around root.
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is extended.
+ */
+int                 p4est_quadrant_is_extended (const p4est_quadrant_t * q);
+
+/** Test if two quadrants are siblings.
+ * \param [in] q1 First quadrant to be tested.
+ * \param [in] q2 Second quadrant to be tested.
+ * \return true if \a q1 is unequal to and a sibling of \a q2.
+ */
+int                 p4est_quadrant_is_sibling (const p4est_quadrant_t * q1,
+                                               const p4est_quadrant_t * q2);
+
+/** Test if two quadrants are siblings.
+ * Descriptive, slower version of \a p4est_quadrant_is_sibling.
+ * For debugging and educational purposes only.
+ */
+int                 p4est_quadrant_is_sibling_D (const p4est_quadrant_t * q1,
+                                                 const p4est_quadrant_t * q2);
+
+/** Test if 4 quadrants are siblings in Morton ordering.
+ */
+int                 p4est_quadrant_is_family (const p4est_quadrant_t * q0,
+                                              const p4est_quadrant_t * q1,
+                                              const p4est_quadrant_t * q2,
+                                              const p4est_quadrant_t * q3);
+
+/** Test if 4 quadrants are siblings in Morton ordering, array version.
+ * \param [in] q   Array of 4 quadrants.
+ */
+int                 p4est_quadrant_is_familyv (const p4est_quadrant_t q[]);
+
+/** Test if 4 quadrants are siblings in Morton ordering, array version.
+ * \param [in] q   Array of 4 pointers to quadrants.
+ */
+int                 p4est_quadrant_is_familypv (p4est_quadrant_t * q[]);
+
+/** Test if a quadrant is the parent of another quadrant.
+ * \param [in] q Quadrant to be tested.
+ * \param [in] r Possible child quadrant.
+ * \return true if \a q is the parent of \a r.
+ */
+int                 p4est_quadrant_is_parent (const p4est_quadrant_t * q,
+                                              const p4est_quadrant_t * r);
+
+/** Test if a quadrant is the parent of another quadrant.
+ * Descriptive, slower version of \a p4est_quadrant_is_parent.
+ * For debugging and educational purposes only.
+ */
+int                 p4est_quadrant_is_parent_D (const p4est_quadrant_t * q,
+                                                const p4est_quadrant_t * r);
+
+/** Test if a quadrant is an ancestor of another quadrant.
+ * \param [in] q Quadrant to be tested.
+ * \param [in] r Descendent quadrant.
+ * \return true if \a q is unequal to and an ancestor of \a r.
+ */
+int                 p4est_quadrant_is_ancestor (const p4est_quadrant_t * q,
+                                                const p4est_quadrant_t * r);
+
+/** Test if a quadrant is an ancestor of another quadrant.
+ * Descriptive, slower version of \a p4est_quadrant_is_ancestor.
+ * Contrary to \a p4est_quadrant_is_ancestor, it aborts for inter-tree q, r.
+ * For debugging and educational purposes only.
+ */
+int                 p4est_quadrant_is_ancestor_D (const p4est_quadrant_t * q,
+                                                  const p4est_quadrant_t * r);
+
+/** Test if two quadrants follow each other in the tree with no holes.
+ * \param [in] q A quadrant
+ * \param [in] r Another quadrant
+ * \return true if \a q is immediately before \a r in the tree.
+ * \note for every \a q there are between 0 and P4EST_MAXLEVEL+1 possible nexts.
+ */
+int                 p4est_quadrant_is_next (const p4est_quadrant_t * q,
+                                            const p4est_quadrant_t * r);
+
+/** Test if two quadrants follow each other in the tree with no holes.
+ * Descriptive, slower version of \a p4est_quadrant_is_next.
+ * For debugging and educational purposes only.
+ */
+int                 p4est_quadrant_is_next_D (const p4est_quadrant_t * q,
+                                              const p4est_quadrant_t * r);
+
+/** Test if a quadrant has at least partial overlap with a tree.
+ */
+int                 p4est_quadrant_overlaps_tree (p4est_tree_t * tree,
+                                                  const p4est_quadrant_t * q);
+
+/** Test if a quadrant is completely contained within a tree.
+ */
+int                 p4est_quadrant_is_inside_tree (p4est_tree_t * tree,
+                                                   const p4est_quadrant_t *
+                                                   q);
+
+/** Compute the ancestor of a quadrant at a given level.
+ * \param [in]  q       Input quadrant.
+ * \param [in]  level   A smaller level than q.
+ * \param [in,out]  r   Existing quadrent whose Morton index will be filled
+ *                      with the ancestor of q at the given level.
+ * \note The quadrant q may point to the same quadrant as r.
+ *       The user_data of r are never modified.
+ */
+void                p4est_quadrant_ancestor (const p4est_quadrant_t * q,
+                                             int level, p4est_quadrant_t * r);
+
+/** Compute the parent of a quadrant.
+ * \param [in]  q Input quadrant.
+ * \param [in,out] r Existing quadrant whose Morton index will be filled
+ *                   with the Morton index of the parent of \a q.
+ *                   Its user_data will be untouched.
+ * \note \a q may point to the same quadrant as \a r.
+         The user_data of \a r is never modified.
+ */
+void                p4est_quadrant_parent (const p4est_quadrant_t * q,
+                                           p4est_quadrant_t * r);
+
+/** Compute a specific sibling of a quadrant.
+ * \param [in]     q  Input quadrant.
+ * \param [in,out] r  Existing quadrant whose Morton index will be filled
+ *                    with the coordinates of sibling no. sibling_id of q.
+ * \param [in]     sibling_id The id of the sibling computed, 0..3.
+ */
+void                p4est_quadrant_sibling (const p4est_quadrant_t * q,
+                                            p4est_quadrant_t * r,
+                                            int sibling_id);
+
+/** Compute the face neighbor of a quadrant.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     face   The face across which to generate the neighbor.
+ * \param [in,out] r      Existing quadrant whose Morton index will be filled.
+ * \note \a q may point to the same quadrant as \a r.
+ */
+void                p4est_quadrant_face_neighbor (const p4est_quadrant_t * q,
+                                                  int face,
+                                                  p4est_quadrant_t * r);
+
+/** Compute the face neighbor of a quadrant, transforming across tree
+ * boundaries if necessary.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     t      Tree that contains \q.
+ * \param [in]     face   The face across which to generate the neighbor.
+ * \param [in,out] r      Existing quadrant whose Morton index will be filled.
+ *                        By convention, if there is no tree across \face,
+ *                        \r has the same Morton index as \q.
+ * \param [in,out] nface  if not NULL, set to the face of \r that neighbors
+ *                        \q.  nface is encoded with orientation information
+ *                        in the same manner as the tree_to_face array in
+ *                        the p4est_connectivity_t struct.
+ * \param [in]     conn   The connectivity structure for the forest.
+ * \return Returns the tree that contains \r.  By convention, if there is no
+ * tree across \face, then -1 is returned.
+ */
+p4est_topidx_t      p4est_quadrant_face_neighbor_extra (const p4est_quadrant_t
+                                                        * q, p4est_topidx_t t,
+                                                        int face,
+                                                        p4est_quadrant_t * r,
+                                                        int *nface,
+                                                        p4est_connectivity_t *
+                                                        conn);
+
+/** Get the smaller face neighbors of \a q.
+ *
+ * Gets the smaller face neighbors, which are half of the size assuming the
+ * 2-1 constant.
+ *
+ * The order of the \a n[i] is given in the Morton ordering.
+ *
+ * \param [in]  q      The quadrant whose face neighbors will be constructed.
+ * \param [in]  face   The face across which to generate the neighbors.
+ * \param [out] n[0]..n[1] Filled with the four smaller face neighbors.
+ * \param [out] nur[0]..nur[1] If not NULL, filled with smallest quadrants
+ *                     that fit in the upper right corners of \a n.
+ */
+void                p4est_quadrant_half_face_neighbors (const p4est_quadrant_t
+                                                        * q, int face,
+                                                        p4est_quadrant_t n[],
+                                                        p4est_quadrant_t
+                                                        nur[]);
+
+/** Create all possible face neighbors of \a q.
+ *
+ * Gets the face neighbors, possible assuming the 2-1 constraint.
+ * If the larger or smaller quadrants do not exist than they are returned
+ * as initialized by P4EST_QUADRANT_INIT.
+ *
+ * The order of \a n[0] through \a n[3] are given in Morton ordering.
+ *
+ * \param [in]  q      The quadrant whose face neighbors will be constructed.
+ * \param [in]  face   The face across which to generate the neighbors.
+ * \param [out] n[0]..n[1] Filled with the smaller possible face neighbors,
+ *                     which are half of the size if they exist
+ *                     or initialized to P4EST_QUADRANT_INIT.
+ * \param [out] n[2]   Filled with the face neighbor, which is the same size.
+ * \param [out] n[3]   Filled with the face neighbor, which is twice the size
+ *                     if it exists or initialized to P4EST_QUADRANT_INIT.
+ */
+void                p4est_quadrant_all_face_neighbors (const p4est_quadrant_t
+                                                       * q, int face,
+                                                       p4est_quadrant_t n[]);
+
+/** Compute the corner neighbor of a quadrant.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     corner The corner across which to generate the neighbor.
+ * \param [in,out] r      Existing quadrant whose Morton index will be filled.
+ * \note \a q may point to the same quadrant as \a r.
+ */
+void                p4est_quadrant_corner_neighbor (const p4est_quadrant_t *
+                                                    q, int corner,
+                                                    p4est_quadrant_t * r);
+
+/** Compute the corner neighbors of a quadrant, transforming across tree
+ * boundaries if necessary.  Only computes neighbors that are not face or edge
+ * neighbors.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     t      Tree that contains \q.
+ * \param [in]     corner The corner across which to generate the neighbor.
+ * \param [in,out] quads  An initialized but empty array where the corner
+ *                        neighbors will be placed.
+ * \param [in,out] treeids An initialized but empty array where the ids of the
+ *                        trees containing the corner neighbors will be placed.
+ * \param [in,out] ncorners if not NULL, filled with the corners of \a quads
+ *                          that neighbor \q.
+ * \param [in]     conn   The connectivity structure for the forest.
+ */
+void                p4est_quadrant_corner_neighbor_extra (const
+                                                          p4est_quadrant_t *
+                                                          q, p4est_locidx_t t,
+                                                          int corner,
+                                                          sc_array_t * quads,
+                                                          sc_array_t *
+                                                          treeids,
+                                                          sc_array_t *
+                                                          ncorners,
+                                                          p4est_connectivity_t
+                                                          * conn);
+
+/** Compute the half size corner neighbor of a quadrant.
+ *
+ * \param [in]  q       The quadrant whose corner neighbor will be constructed.
+ * \param [in]  corner  The corner across which to generate the neighbor.
+ * \param [out] r       Morton index filled with the half size corner neighbor.
+ */
+void                p4est_quadrant_half_corner_neighbor (const
+                                                         p4est_quadrant_t * q,
+                                                         int corner,
+                                                         p4est_quadrant_t *
+                                                         r);
+
+/** Compute the corner node of a quadrant.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     corner The corner across which to generate the neighbor.
+ * \param [in,out] r      Node that will not be clamped inside.
+ * \note \a q may point to the same quadrant as \a r.
+ */
+void                p4est_quadrant_corner_node (const p4est_quadrant_t * q,
+                                                int corner,
+                                                p4est_quadrant_t * r);
+
+/** Compute the 4 children of a quadrant.
+ * \param [in]     q  Input quadrant.
+ * \param [in,out] c0 First computed child.
+ *                    \a q may point to the same quadrant as \a c0.
+ * \note The user_data of \a c0, c1, c2, c3 is never modified.
+ */
+void                p4est_quadrant_children (const p4est_quadrant_t * q,
+                                             p4est_quadrant_t * c0,
+                                             p4est_quadrant_t * c1,
+                                             p4est_quadrant_t * c2,
+                                             p4est_quadrant_t * c3);
+
+/** Compute the 4 children of a quadrant, array version.
+ * \param [in]     q  Input quadrant.
+ * \param [in,out] c  The 4 computed children in z-order.
+ *                    q may point to the same quadrant as c[0].
+ * \note The user_data of c[i] is never modified.
+ */
+void                p4est_quadrant_childrenv (const p4est_quadrant_t * q,
+                                              p4est_quadrant_t c[]);
+
+/** Compute the 4 children of a quadrant, array version.
+ * \param [in]     q  Input quadrant.
+ * \param [in,out] c  Pointers to the 4 computed children in z-order.
+ *                    q may point to the same quadrant as c[0].
+ * \note The user_data of c[i] is never modified.
+ */
+void                p4est_quadrant_childrenpv (const p4est_quadrant_t * q,
+                                               p4est_quadrant_t * c[]);
+
+/** Compute the first descendant of a quadrant on a given level.
+ * \param [in]  q      Input quadrant.
+ * \param [out] fd     First descendant of \a q on level \a level.
+ * \param [in]  level  Level must be greater equal than q's level.
+ */
+void                p4est_quadrant_first_descendant (const p4est_quadrant_t *
+                                                     q, p4est_quadrant_t * fd,
+                                                     int level);
+
+/** Compute the last descendant of a quadrant on a given level.
+ * \param [in]  q      Input quadrant.
+ * \param [out] ld     Last descendant of \a q on level \a level.
+ * \param [in]  level  Level must be greater equal than q's level.
+ */
+void                p4est_quadrant_last_descendant (const p4est_quadrant_t *
+                                                    q, p4est_quadrant_t * ld,
+                                                    int level);
+
+/** Compute the descendant of a quadrant touching a given corner.
+ * \param [in]     q   Input quadrant.
+ * \param [in,out] r   Existing quadrant whose Morton index will be filled.
+ *                     Its user_data will be untouched.
+ * \param [in]     c   The corner of \a q that \a r touches.
+ * \param [in] level   The size of \a r.
+ */
+void                p4est_quadrant_corner_descendant (const p4est_quadrant_t *
+                                                      q, p4est_quadrant_t * r,
+                                                      int c, int level);
+
+/** Computes the nearest common ancestor of two quadrants in the same tree.
+ * \param [in]     q1 First input quadrant.
+ * \param [in]     q2 Second input quadrant.
+ * \param [in,out] r Existing quadrant whose Morton index will be filled.
+ *                   Its user_data will be untouched.
+ * \note \a q1, \a q2, \a r may point to the same quadrant.
+ *       The user_data of \a r is never modified.
+ */
+void                p4est_nearest_common_ancestor (const p4est_quadrant_t *
+                                                   q1,
+                                                   const p4est_quadrant_t *
+                                                   q2, p4est_quadrant_t * r);
+
+/** Computes the nearest common ancestor of two quadrants in the same tree.
+ * Descriptive, slower version of \a p4est_nearest_common_ancestor.
+ * For debugging and educationial purposes only.
+ */
+void                p4est_nearest_common_ancestor_D (const p4est_quadrant_t *
+                                                     q1,
+                                                     const p4est_quadrant_t *
+                                                     q2,
+                                                     p4est_quadrant_t * r);
+
+/** Transforms a quadrant/node across a face between trees.
+ * \param [in]     q        Input quadrant/non-clamped node.
+ * \param [in,out] r        Quadrant/node whose Morton index will be filled.
+ * \param [in] ftransform   This array holds 9 integers.
+ *             [0,2]        The coordinate axis sequence of the origin face.
+ *             [3,5]        The coordinate axis sequence of the target face.
+ *             [6,8]        Edge reverse flag for axis 0; face code for 1.
+ *             [1,4,7]      0 (unused for compatibility with 3D).
+ * \note \a q and \q r may NOT point to the same quadrant structure.
+ */
+void                p4est_quadrant_transform_face (const p4est_quadrant_t * q,
+                                                   p4est_quadrant_t * r,
+                                                   const int ftransform[]);
+
+/** Checks if a quadrant touches a corner (diagonally inside or outside).
+ */
+int                 p4est_quadrant_touches_corner (const p4est_quadrant_t * q,
+                                                   int corner, int inside);
+
+/** Move a quadrant inside or diagonally outside a corner position.
+ * \param [in,out] q        This quadrant only requires a valid level.
+ * \param [in]     icorner  Number of the corner in 0..3.
+ * \param [int]    inside   Boolean flag for inside or diagonally outside.
+ */
+void                p4est_quadrant_transform_corner (p4est_quadrant_t * q,
+                                                     int icorner, int inside);
+
+/** Shifts a quadrant until it touches the specified corner from the inside.
+ * \param [in]     q          Valid input quadrant.
+ * \param [in,out] r          Quadrant whose Morton index will be filled.
+ * \param [in]     corner     Corner index.
+ */
+void                p4est_quadrant_shift_corner (const p4est_quadrant_t * q,
+                                                 p4est_quadrant_t * r,
+                                                 int corner);
+
+/** Computes the linear position of a quadrant in a uniform grid.
+ * \param [in] quadrant  Quadrant whose id will be computed.
+ * \return Returns the linear position of this quadrant on a grid.
+ * \note This is the inverse operation of p4est_quadrant_set_morton.
+ *       The user_data of \a quadrant is never modified.
+ */
+uint64_t            p4est_quadrant_linear_id (const p4est_quadrant_t *
+                                              quadrant, int level);
+
+/** Set quadrant Morton indices based on linear position in uniform grid.
+ * \param [in,out] quadrant  Quadrant whose Morton indices will be set.
+ * \param [in]     id        The linear position of this quadrant on a grid.
+ * \note This is the inverse operation of p4est_quadrant_linear_id.
+ *       The user_data of \a quadrant is never modified.
+ */
+void                p4est_quadrant_set_morton (p4est_quadrant_t * quadrant,
+                                               int level, uint64_t id);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_BITS_H */
diff --git a/src/p4est_communication.c b/src/p4est_communication.c
new file mode 100644
index 0000000..e184d18
--- /dev/null
+++ b/src/p4est_communication.c
@@ -0,0 +1,593 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef P4_TO_P8
+#include <p8est_communication.h>
+#include <p8est_bits.h>
+#else
+#include <p4est_communication.h>
+#include <p4est_bits.h>
+#endif /* !P4_TO_P8 */
+#ifdef P4EST_HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+void
+p4est_comm_count_quadrants (p4est_t * p4est)
+{
+  int                 mpiret;
+  p4est_gloidx_t      qlocal = p4est->local_num_quadrants;
+  p4est_gloidx_t     *global_first_quadrant = p4est->global_first_quadrant;
+  int                 i;
+  const int           num_procs = p4est->mpisize;
+
+  global_first_quadrant[0] = 0;
+  mpiret = sc_MPI_Allgather (&qlocal, 1, P4EST_MPI_GLOIDX,
+                             global_first_quadrant + 1, 1, P4EST_MPI_GLOIDX,
+                             p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  for (i = 0; i < num_procs; ++i) {
+    global_first_quadrant[i + 1] += global_first_quadrant[i];
+  }
+  p4est->global_num_quadrants = global_first_quadrant[num_procs];
+}
+
+void
+p4est_comm_global_partition (p4est_t * p4est, p4est_quadrant_t * first_quad)
+{
+  const int           num_procs = p4est->mpisize;
+  const p4est_topidx_t num_trees = p4est->connectivity->num_trees;
+  int                 i;
+  int                 mpiret;
+  const p4est_topidx_t first_tree = p4est->first_local_tree;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *quadrant;
+  p4est_quadrant_t   *pi, input;
+
+  SC_BZERO (&p4est->global_first_position[num_procs], 1);
+  p4est->global_first_position[num_procs].level = P4EST_QMAXLEVEL;
+  p4est->global_first_position[num_procs].p.which_tree = num_trees;
+
+  SC_BZERO (&input, 1);
+  if (first_tree < 0) {
+    /* i don't have any quadrants, send negative values */
+    P4EST_ASSERT (first_tree == -1 && p4est->last_local_tree == -2);
+    input.x = -1;
+    input.y = -1;
+#ifdef P4_TO_P8
+    input.z = -1;
+#endif
+  }
+  else {
+    /* send values corresponding to my first quadrant */
+    if (first_quad != NULL) {
+      tree = NULL;
+      quadrant = first_quad;
+    }
+    else {
+      tree = p4est_tree_array_index (p4est->trees, first_tree);
+      quadrant = p4est_quadrant_array_index (&tree->quadrants, 0);
+    }
+    input.x = quadrant->x;
+    input.y = quadrant->y;
+#ifdef P4_TO_P8
+    input.z = quadrant->z;
+#endif
+  }
+  input.level = P4EST_QMAXLEVEL;
+  input.p.which_tree = first_tree;
+  mpiret = sc_MPI_Allgather (&input, (int) sizeof (p4est_quadrant_t),
+                             sc_MPI_BYTE, p4est->global_first_position,
+                             (int) sizeof (p4est_quadrant_t), sc_MPI_BYTE,
+                             p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  /* correct for processors that don't have any quadrants */
+  for (i = num_procs - 1; i >= 0; --i) {
+    pi = &p4est->global_first_position[i];
+    if (pi->p.which_tree < 0) {
+      P4EST_ASSERT (pi->x == -1 && pi->y == -1);
+#ifdef P4_TO_P8
+      P4EST_ASSERT (pi->z == -1);
+#endif
+      memcpy (pi, pi + 1, sizeof (p4est_quadrant_t));
+    }
+    P4EST_ASSERT (pi->x >= 0 && pi->y >= 0);
+#ifdef P4_TO_P8
+    P4EST_ASSERT (pi->z >= 0);
+#endif
+    P4EST_ASSERT (pi->p.which_tree >= 0 && pi->level == P4EST_QMAXLEVEL);
+  }
+}
+
+void
+p4est_comm_count_pertree (p4est_t * p4est, p4est_gloidx_t * pertree)
+{
+  const int           num_procs = p4est->mpisize;
+  const int           rank = p4est->mpirank;
+  const p4est_gloidx_t *gfq = p4est->global_first_quadrant;
+  const p4est_quadrant_t *gfp = p4est->global_first_position;
+  const p4est_topidx_t num_trees = p4est->connectivity->num_trees;
+  int                 mpiret;
+  int                 p;
+  int                 mycount, c, addtomytree;
+  int                *treecount, *treeoffset;
+  p4est_topidx_t      t;
+  p4est_locidx_t      recvbuf, sendbuf;
+  p4est_gloidx_t     *mypertree;
+  sc_MPI_Request      req_recv, req_send;
+  sc_MPI_Status       status;
+  p4est_tree_t       *tree;
+#ifdef P4EST_ENABLE_DEBUG
+  const p4est_quadrant_t *q;
+#endif
+
+  /* Tip off valgrind in case input array is too small */
+  pertree[num_trees] = 0;
+
+  /*
+   * Determine which trees each rank will be counting.
+   * A tree is counted by the processor that starts on its first quadrant,
+   * even if this processor is empty.
+   */
+  treecount = P4EST_ALLOC (int, num_procs + 1);
+  treeoffset = P4EST_ALLOC (int, num_procs + 1);
+  p = 0;
+  t = 0;
+  treecount[0] = 1;
+  treeoffset[0] = 0;
+  for (;;) {
+    /* Invariant: Rank p is the first that mentions tree t in gfp[p]
+       and the ownership of t has been assigned to p or p - 1 */
+    P4EST_ASSERT (gfp[p].p.which_tree == t);
+    P4EST_ASSERT (p == 0 || gfp[p - 1].p.which_tree < t);
+    do {
+      treecount[++p] = 0;
+    }
+    while (gfp[p].p.which_tree == t);
+    /* Assign the trees before the next first quadrant */
+    P4EST_ASSERT (t < gfp[p].p.which_tree);
+    for (++t; t < gfp[p].p.which_tree; ++t) {
+      ++treecount[p - 1];
+    }
+    if (t < num_trees) {
+      P4EST_ASSERT (p < num_procs);
+      /* Check if the processor has the beginning of the tree */
+      if (gfp[p].x == 0 && gfp[p].y == 0
+#ifdef P4_TO_P8
+          && gfp[p].z == 0
+#endif
+        ) {
+        ++treecount[p];
+      }
+      else {
+        ++treecount[p - 1];
+      }
+    }
+    else {
+      while (p < num_procs) {
+        treecount[++p] = 0;
+      }
+      break;
+    }
+  }
+  P4EST_ASSERT (p == num_procs);
+  P4EST_ASSERT (t == num_trees);
+  P4EST_ASSERT (treecount[num_procs] == 0);
+  for (p = 0; p < num_procs; ++p) {
+    treeoffset[p + 1] = treeoffset[p] + treecount[p];
+  }
+  P4EST_ASSERT ((p4est_topidx_t) treeoffset[num_procs] == num_trees);
+  mycount = treecount[rank];
+#ifdef P4EST_ENABLE_DEBUG
+  P4EST_ASSERT (p4est->first_local_tree <= treeoffset[rank]);
+  P4EST_ASSERT (gfq[rank + 1] - gfq[rank] ==
+                (p4est_gloidx_t) p4est->local_num_quadrants);
+  for (c = 0; c < mycount; ++c) {
+    t = (p4est_topidx_t) (treeoffset[rank] + c);
+    P4EST_ASSERT (rank == 0 || gfp[rank - 1].p.which_tree < t);
+    if (p4est->local_num_quadrants > 0) {
+      tree = p4est_tree_array_index (p4est->trees, t);
+      q = p4est_quadrant_array_index (&tree->quadrants, 0);
+    }
+    else {
+      q = gfp + rank;
+    }
+    P4EST_ASSERT (q->x == 0 && q->y == 0);
+#ifdef P4_TO_P8
+    P4EST_ASSERT (q->z == 0);
+#endif
+  }
+#endif
+
+  /* Go through trees this rank is responsible for and collect information */
+  recvbuf = sendbuf = -1;
+  addtomytree = -1;
+  mypertree = P4EST_ALLOC (p4est_gloidx_t, mycount);
+  for (c = 0; c < mycount; ++c) {
+    /* Rank owns at least the first quadrant on this tree */
+    t = (p4est_topidx_t) (treeoffset[rank] + c);
+    tree = p4est_tree_array_index (p4est->trees, t);
+    mypertree[c] = (p4est_gloidx_t) tree->quadrants.elem_count;
+    if (c == mycount - 1) {
+      /* Only the last tree in the counted list may be partially owned */
+      for (p = rank + 1; p < num_procs && treecount[p] == 0; ++p) {
+        P4EST_ASSERT (p < num_procs);
+      }
+      mypertree[c] += gfq[p] - gfq[rank + 1];
+      if (gfp[p].p.which_tree == t) {
+        P4EST_ASSERT (p < num_procs);
+        /* Processor p has part of this tree too and needs to tell me */
+        mpiret = sc_MPI_Irecv (&recvbuf, 1, P4EST_MPI_LOCIDX, p,
+                               P4EST_COMM_COUNT_PERTREE, p4est->mpicomm,
+                               &req_recv);
+        SC_CHECK_MPI (mpiret);
+        addtomytree = c;
+      }
+      else {
+        P4EST_ASSERT (p <= num_procs);
+        P4EST_ASSERT (gfp[p].p.which_tree == t + 1);
+      }
+    }
+  }
+  if (mycount > 0 && (t = gfp[rank].p.which_tree) < treeoffset[rank]) {
+    /* Send information to processor that counts my first local quadrants */
+    P4EST_ASSERT (rank > 0 && p4est->first_local_tree == t);
+    tree = p4est_tree_array_index (p4est->trees, t);
+    /* Always below the 32bit limit since this is processor-local data */
+    sendbuf = (p4est_locidx_t) tree->quadrants.elem_count;
+    for (p = rank - 1; treecount[p] == 0; --p) {
+      P4EST_ASSERT (p > 0);
+    }
+    mpiret = sc_MPI_Isend (&sendbuf, 1, P4EST_MPI_LOCIDX, p,
+                           P4EST_COMM_COUNT_PERTREE, p4est->mpicomm,
+                           &req_send);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  /* Complete MPI operations and cumulative count */
+  if (addtomytree >= 0) {
+    mpiret = sc_MPI_Wait (&req_recv, &status);
+    SC_CHECK_MPI (mpiret);
+    mypertree[addtomytree] += (p4est_gloidx_t) recvbuf;
+  }
+  pertree[0] = 0;
+  mpiret = sc_MPI_Allgatherv (mypertree, mycount, P4EST_MPI_GLOIDX,
+                              pertree + 1, treecount, treeoffset,
+                              P4EST_MPI_GLOIDX, p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+  for (c = 0; c < (int) num_trees; ++c) {
+    pertree[c + 1] += pertree[c];
+  }
+  if (sendbuf >= 0) {
+    mpiret = sc_MPI_Wait (&req_send, &status);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  /* Clean up */
+  P4EST_FREE (treecount);
+  P4EST_FREE (treeoffset);
+  P4EST_FREE (mypertree);
+}
+
+int
+p4est_comm_is_owner (p4est_t * p4est, p4est_locidx_t which_tree,
+                     const p4est_quadrant_t * q, int rank)
+{
+  p4est_topidx_t      ctree;
+  p4est_quadrant_t    cur;
+  const p4est_quadrant_t *global_first_position =
+    p4est->global_first_position;
+
+  cur.level = P4EST_QMAXLEVEL;
+  P4EST_ASSERT (0 <= which_tree &&
+                which_tree < p4est->connectivity->num_trees);
+  P4EST_ASSERT (0 <= rank && rank < p4est->mpisize);
+  P4EST_ASSERT (p4est_quadrant_is_node (q, 1) || p4est_quadrant_is_valid (q));
+
+  /* check if q is on a lower processor than guess */
+  ctree = global_first_position[rank].p.which_tree;
+  cur.x = global_first_position[rank].x;
+  cur.y = global_first_position[rank].y;
+#ifdef P4_TO_P8
+  cur.z = global_first_position[rank].z;
+#endif
+  if (which_tree < ctree ||
+      (which_tree == ctree &&
+       (p4est_quadrant_compare (q, &cur) < 0 &&
+        (q->x != cur.x || q->y != cur.y
+#ifdef P4_TO_P8
+         || q->z != cur.z
+#endif
+        )))) {
+    return 0;
+  }
+
+  /* check if q is on a higher processor than guess */
+  ctree = global_first_position[rank + 1].p.which_tree;
+  cur.x = global_first_position[rank + 1].x;
+  cur.y = global_first_position[rank + 1].y;
+#ifdef P4_TO_P8
+  cur.z = global_first_position[rank + 1].z;
+#endif
+  if (which_tree > ctree ||
+      (which_tree == ctree &&
+       (p4est_quadrant_compare (&cur, q) <= 0 ||
+        (q->x == cur.x && q->y == cur.y
+#ifdef P4_TO_P8
+         && q->z == cur.z
+#endif
+        )))) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int
+p4est_comm_find_owner (p4est_t * p4est, p4est_locidx_t which_tree,
+                       const p4est_quadrant_t * q, int guess)
+{
+  const int           num_procs = p4est->mpisize;
+  const p4est_quadrant_t *global_first_position =
+    p4est->global_first_position;
+  int                 proc_low, proc_high;
+  p4est_topidx_t      ctree;
+  p4est_quadrant_t    cur;
+
+  P4EST_ASSERT (0 <= which_tree &&
+                which_tree < p4est->connectivity->num_trees);
+  P4EST_ASSERT (p4est_quadrant_is_node (q, 1) || p4est_quadrant_is_valid (q));
+
+  proc_low = 0;
+  proc_high = num_procs - 1;
+  cur.level = P4EST_QMAXLEVEL;
+
+  for (;;) {
+    P4EST_ASSERT (proc_low <= proc_high);
+    P4EST_ASSERT (0 <= proc_low && proc_low < num_procs);
+    P4EST_ASSERT (0 <= proc_high && proc_high < num_procs);
+    P4EST_ASSERT (proc_low <= guess && guess <= proc_high);
+
+    /* check if q is on a lower processor than guess */
+    ctree = global_first_position[guess].p.which_tree;
+    cur.x = global_first_position[guess].x;
+    cur.y = global_first_position[guess].y;
+#ifdef P4_TO_P8
+    cur.z = global_first_position[guess].z;
+#endif
+    if (which_tree < ctree ||
+        (which_tree == ctree &&
+         (p4est_quadrant_compare (q, &cur) < 0 &&
+          (q->x != cur.x || q->y != cur.y
+#ifdef P4_TO_P8
+           || q->z != cur.z
+#endif
+          )))) {
+      proc_high = guess - 1;
+      guess = (proc_low + proc_high + 1) / 2;
+      continue;
+    }
+
+    /* check if q is on a higher processor than guess */
+    ctree = global_first_position[guess + 1].p.which_tree;
+    cur.x = global_first_position[guess + 1].x;
+    cur.y = global_first_position[guess + 1].y;
+#ifdef P4_TO_P8
+    cur.z = global_first_position[guess + 1].z;
+#endif
+    if (which_tree > ctree ||
+        (which_tree == ctree &&
+         (p4est_quadrant_compare (&cur, q) <= 0 ||
+          (q->x == cur.x && q->y == cur.y
+#ifdef P4_TO_P8
+           && q->z == cur.z
+#endif
+          )))) {
+      proc_low = guess + 1;
+      guess = (proc_low + proc_high) / 2;
+      continue;
+    }
+
+    /* otherwise guess is the correct processor */
+    break;
+  }
+
+  /* make sure we found a valid processor with nonzero quadrant count */
+  P4EST_ASSERT (0 <= guess && guess < num_procs);
+  P4EST_ASSERT (memcmp (&global_first_position[guess],
+                        &global_first_position[guess + 1],
+                        sizeof (p4est_quadrant_t)) != 0);
+  return guess;
+}
+
+void
+p4est_comm_tree_info (p4est_t * p4est, p4est_locidx_t which_tree,
+                      int full_tree[], int tree_contact[],
+                      const p4est_quadrant_t ** pfirst_pos,
+                      const p4est_quadrant_t ** pnext_pos)
+{
+  const p4est_quadrant_t *first_pos, *next_pos;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  int                 face;
+
+  P4EST_ASSERT (p4est->first_local_tree <= which_tree);
+  P4EST_ASSERT (which_tree <= p4est->last_local_tree);
+
+  first_pos = &p4est->global_first_position[p4est->mpirank];
+  P4EST_ASSERT (first_pos->level == P4EST_QMAXLEVEL);
+  full_tree[0] = (which_tree > p4est->first_local_tree ||
+                  (first_pos->x == 0 && first_pos->y == 0
+#ifdef P4_TO_P8
+                   && first_pos->z == 0
+#endif
+                  ));
+
+  next_pos = &p4est->global_first_position[p4est->mpirank + 1];
+  P4EST_ASSERT (next_pos->level == P4EST_QMAXLEVEL);
+  full_tree[1] = (which_tree < p4est->last_local_tree ||
+                  (next_pos->x == 0 && next_pos->y == 0
+#ifdef P4_TO_P8
+                   && next_pos->z == 0
+#endif
+                  ));
+
+  if (tree_contact != NULL) {
+    for (face = 0; face < P4EST_FACES; ++face) {
+      tree_contact[face] =
+        (conn->tree_to_tree[P4EST_FACES * which_tree + face] != which_tree
+         || (int) conn->tree_to_face[P4EST_FACES * which_tree + face] !=
+         face);
+    }
+  }
+
+  if (pfirst_pos != NULL) {
+    *pfirst_pos = first_pos;
+  }
+  if (pnext_pos != NULL) {
+    *pnext_pos = next_pos;
+  }
+}
+
+int
+p4est_comm_neighborhood_owned (p4est_t * p4est, p4est_locidx_t which_tree,
+                               int full_tree[], int tree_contact[],
+                               p4est_quadrant_t * q)
+{
+  const p4est_qcoord_t qh = P4EST_QUADRANT_LEN (q->level);
+  const int           rank = p4est->mpirank;
+  int                 n0_proc, n1_proc;
+  p4est_quadrant_t    n0, n1;
+
+  if (full_tree[0] && full_tree[1]) {
+    /* need only to consider boundary quadrants */
+    if (!((tree_contact[0] && q->x == 0) ||
+          (tree_contact[1] && q->x == P4EST_ROOT_LEN - qh) ||
+          (tree_contact[2] && q->y == 0) ||
+          (tree_contact[3] && q->y == P4EST_ROOT_LEN - qh) ||
+#ifdef P4_TO_P8
+          (tree_contact[4] && q->z == 0) ||
+          (tree_contact[5] && q->z == P4EST_ROOT_LEN - qh) ||
+#endif
+          0)) {
+      return 1;
+    }
+  }
+  else {
+    /* test lowest and highest neighbor first */
+    n0.x = q->x - qh;
+    n0.y = q->y - qh;
+#ifdef P4_TO_P8
+    n0.z = q->z - qh;
+#endif
+    n0.level = q->level;
+    if (n0.x >= 0 && n0.y >= 0
+#ifdef P4_TO_P8
+        && n0.z >= 0
+#endif
+      ) {
+      n1.x = q->x + qh;
+      n1.y = q->y + qh;
+#ifdef P4_TO_P8
+      n1.z = q->z + qh;
+#endif
+      n1.level = q->level;
+      if (n1.x < P4EST_ROOT_LEN && n1.y < P4EST_ROOT_LEN
+#ifdef P4_TO_P8
+          && n1.z < P4EST_ROOT_LEN
+#endif
+        ) {
+        n0_proc = p4est_comm_find_owner (p4est, which_tree, &n0, rank);
+        if (n0_proc == rank) {
+          p4est_quadrant_last_descendant (&n1, &n0, P4EST_QMAXLEVEL);
+          n1_proc = p4est_comm_find_owner (p4est, which_tree, &n0, rank);
+          if (n1_proc == rank) {
+            return 1;
+          }
+        }
+      }
+    }
+  }
+
+  return 0;
+}
+
+int
+p4est_comm_sync_flag (p4est_t * p4est, int flag, sc_MPI_Op operation)
+{
+  int8_t              lbyte, gbyte;
+  int                 mpiret;
+
+  P4EST_ASSERT (operation == sc_MPI_BAND || operation == sc_MPI_BOR);
+
+  lbyte = (int8_t) (flag ? 1 : 0);
+  mpiret = sc_MPI_Allreduce (&lbyte, &gbyte, 1, sc_MPI_BYTE, operation,
+                             p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  return (int) gbyte;
+}
+
+unsigned
+p4est_comm_checksum (p4est_t * p4est, unsigned local_crc, size_t local_bytes)
+{
+#ifdef P4EST_HAVE_ZLIB
+  uLong               crc = (uLong) local_crc;
+
+#ifdef P4EST_ENABLE_MPI
+  int                 mpiret;
+  int                 p;
+  uint64_t            send[2];
+  uint64_t           *gather;
+
+  send[0] = (uint64_t) local_crc;
+  send[1] = (uint64_t) local_bytes;
+  gather = NULL;
+  if (p4est->mpirank == 0) {
+    gather = P4EST_ALLOC (uint64_t, 2 * p4est->mpisize);
+  }
+  mpiret = MPI_Gather (send, 2, MPI_LONG_LONG_INT,
+                       gather, 2, MPI_LONG_LONG_INT, 0, p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  if (p4est->mpirank == 0) {
+    for (p = 1; p < p4est->mpisize; ++p) {
+      crc = adler32_combine (crc, (uLong) gather[2 * p + 0],
+                             (z_off_t) gather[2 * p + 1]);
+    }
+    P4EST_FREE (gather);
+  }
+  else {
+    crc = 0;
+  }
+#endif /* P4EST_ENABLE_MPI */
+
+  return (unsigned) crc;
+#else
+  sc_abort_collective
+    ("Configure did not find a recent enough zlib.  Abort.\n");
+
+  return 0;
+#endif /* !P4EST_HAVE_ZLIB */
+}
diff --git a/src/p4est_communication.h b/src/p4est_communication.h
new file mode 100644
index 0000000..eb2a007
--- /dev/null
+++ b/src/p4est_communication.h
@@ -0,0 +1,159 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_COMMUNICATION_H
+#define P4EST_COMMUNICATION_H
+
+#include <p4est.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef enum
+{
+  P4EST_COMM_COUNT_PERTREE = 1,
+  P4EST_COMM_BALANCE_FIRST_COUNT,
+  P4EST_COMM_BALANCE_FIRST_LOAD,
+  P4EST_COMM_BALANCE_SECOND_COUNT,
+  P4EST_COMM_BALANCE_SECOND_LOAD,
+  P4EST_COMM_PARTITION_GIVEN,
+  P4EST_COMM_PARTITION_WEIGHTED_LOW,
+  P4EST_COMM_PARTITION_WEIGHTED_HIGH,
+  P4EST_COMM_PARTITION_CORRECTION,
+  P4EST_COMM_GHOST_COUNT,
+  P4EST_COMM_GHOST_LOAD,
+  P4EST_COMM_GHOST_EXCHANGE,
+  P4EST_COMM_GHOST_EXPAND_COUNT,
+  P4EST_COMM_GHOST_EXPAND_LOAD,
+  P4EST_COMM_GHOST_SUPPORT_COUNT,
+  P4EST_COMM_GHOST_SUPPORT_LOAD,
+  P4EST_COMM_GHOST_CHECKSUM,
+  P4EST_COMM_NODES_QUERY,
+  P4EST_COMM_NODES_REPLY,
+  P4EST_COMM_SAVE,
+  P4EST_COMM_LNODES_TEST,
+  P4EST_COMM_LNODES_PASS,
+  P4EST_COMM_LNODES_OWNED,
+  P4EST_COMM_LNODES_ALL
+}
+p4est_comm_tag_t;
+
+/** Caculate the number and partition of quadrents.
+ * \param [in,out] p4est  Adds all \c p4est->local_num_quadrant counters and
+ *                        puts cumulative sums in p4est->global_first_quadrant.
+ */
+void                p4est_comm_count_quadrants (p4est_t * p4est);
+
+/** Distribute the global partition boundaries.
+ * \param [in,out] p4est        Fills \c p4est->global_first_position.
+ *                              p4est->first_local_tree must be set correctly.
+ *                              If this processor is not empty and
+ *                              first_quad is NULL, the first quadrant
+ *                              of the first local tree must be set correctly.
+ * \param [in] first_quad       If not NULL will be used as first quadrant.
+ */
+void                p4est_comm_global_partition (p4est_t * p4est,
+                                                 p4est_quadrant_t *
+                                                 first_quad);
+
+/** Compute and distribute the cumulative number of quadrants per tree.
+ * \param [in] p4est    This p4est needs to have correct values for
+ *                      global_first_quadrant and global_first_position.
+ * \param [in,out] pertree      On input, memory for num_trees + 1 numbers.
+ *                              On output, the cumulative quadrant counts.
+ */
+void                p4est_comm_count_pertree (p4est_t * p4est,
+                                              p4est_gloidx_t * pertree);
+
+/** Tests ownershop of a quadrant via p4est->global_first_position.
+ * Assumes a tree with no overlaps.
+ * \param [in] rank    Rank whose ownership is tested.
+ * \return true if rank is the owner.
+ */
+int                 p4est_comm_is_owner (p4est_t * p4est,
+                                         p4est_locidx_t which_tree,
+                                         const p4est_quadrant_t * q,
+                                         int rank);
+
+/** Searches the owner of a quadrant via p4est->global_first_position.
+ * Assumes a tree with no overlaps.
+ * \param [in] guess   Initial guess for the search.
+ * \return Returns the processor id of the owner.
+ */
+int                 p4est_comm_find_owner (p4est_t * p4est,
+                                           p4est_locidx_t which_tree,
+                                           const p4est_quadrant_t * q,
+                                           int guess);
+
+/** Computes information about a tree being fully owned.
+ * This is determined separately for the beginning and end of the tree.
+ * \param [in] p4est            The p4est to work on.
+ * \param [in] which_tree       The tree in question must be partially owned.
+ * \param [out] full_tree[2]    Full ownership of beginning and end of tree.
+ * \param [out] tree_contact[4] True if there are neighbors across the face.
+ * \param [out] firstq          Smallest possible first quadrant on this core.
+ * \param [out] nextq           Smallest possible first quadrant on next core.
+ *                          Any of tree_contact, firstq and nextq may be NULL.
+ */
+void                p4est_comm_tree_info (p4est_t * p4est,
+                                          p4est_locidx_t which_tree,
+                                          int full_tree[],
+                                          int tree_contact[],
+                                          const p4est_quadrant_t ** firstq,
+                                          const p4est_quadrant_t ** nextq);
+
+/** Test if the 3x3 neighborhood of a quadrant is owned by this processor.
+ * \param [in] p4est            The p4est to work on.
+ * \param [in] which_tree       The tree index to work on.
+ * \param [in] full_tree[2]     Flags as computed by p4est_comm_tree_info.
+ * \param [in] tree_contact[4]  Flags as computed by p4est_comm_tree_info.
+ * \param [in] q                The quadrant to be checked.
+ * \return          Returns true iff this quadrant's 3x3 neighborhood is owned.
+ */
+int                 p4est_comm_neighborhood_owned (p4est_t * p4est,
+                                                   p4est_locidx_t which_tree,
+                                                   int full_tree[],
+                                                   int tree_contact[],
+                                                   p4est_quadrant_t * q);
+
+/** Evaluates true/false of a flag among processors.
+ * \param [in] p4est        The MPI communicator of this p4est will be used.
+ * \param [in] flag         The variable to communicate.
+ * \param [in] operation    Either sc_MPI_BAND or sc_MPI_BOR (not used bitwise).
+ * \return          Returns the logical AND resp. OR of all processors' flags.
+ */
+int                 p4est_comm_sync_flag (p4est_t * p4est,
+                                          int flag, sc_MPI_Op operation);
+
+/** Compute a parallel checksum out of local checksums.
+ * \param [in] p4est       The MPI information of this p4est will be used.
+ * \param [in] local_crc   Locally computed adler32 checksum.
+ * \param [in] local_bytes Number of bytes used for local checksum.
+ * \return                 Parallel checksum on rank 0, 0 otherwise.
+ */
+unsigned            p4est_comm_checksum (p4est_t * p4est,
+                                         unsigned local_crc,
+                                         size_t local_bytes);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_COMMUNICATION_H */
diff --git a/src/p4est_connectivity.c b/src/p4est_connectivity.c
new file mode 100644
index 0000000..0466b59
--- /dev/null
+++ b/src/p4est_connectivity.c
@@ -0,0 +1,4164 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef P4_TO_P8
+#include <p8est_connectivity.h>
+#else
+#include <p4est_connectivity.h>
+#endif
+#include <sc_io.h>
+#ifdef P4EST_WITH_METIS
+#include <metis.h>
+#endif
+
+#ifndef P4_TO_P8
+
+/* *INDENT-OFF* */
+const int           p4est_face_corners[4][2] =
+{{ 0, 2 },
+ { 1, 3 },
+ { 0, 1 },
+ { 2, 3 }};
+const int           p4est_face_dual[4] = { 1, 0, 3, 2 };
+
+const int           p4est_corner_faces[4][2] =
+{{ 0, 2 },
+ { 1, 2 },
+ { 0, 3 },
+ { 1, 3 }};
+const int           p4est_corner_face_corners[4][4] =
+{{  0, -1,  0, -1 },
+ { -1,  0,  1, -1 },
+ {  1, -1, -1,  0 },
+ { -1,  1, -1,  1 }};
+
+const int           p4est_child_corner_faces[4][4] =
+{{ -1,  2,  0, -1 },
+ {  2, -1, -1,  1 },
+ {  0, -1, -1,  3 },
+ { -1,  1,  3, -1 }};
+/* *INDENT-ON* */
+
+#endif /* !P4_TO_P8 */
+
+int
+p4est_connectivity_face_neighbor_corner_orientation (int c, int f,
+                                                     int nf, int o)
+{
+  int                 fc, nfc;
+#ifdef P4_TO_P8
+  int                 pref, pset;
+#endif
+
+  P4EST_ASSERT (0 <= c && c < P4EST_CHILDREN);
+  P4EST_ASSERT (0 <= f && f < P4EST_FACES);
+  P4EST_ASSERT (0 <= nf && nf < P4EST_FACES);
+  P4EST_ASSERT (0 <= o && o < P4EST_HALF);
+
+  fc = p4est_corner_face_corners[c][f];
+  P4EST_ASSERT (0 <= fc && fc < P4EST_HALF);
+
+#ifndef P4_TO_P8
+  nfc = fc ^ o;
+#else
+  pref = p8est_face_permutation_refs[f][nf];
+  pset = p8est_face_permutation_sets[pref][o];
+  nfc = p8est_face_permutations[pset][fc];
+#endif
+  P4EST_ASSERT (0 <= nfc && nfc < P4EST_HALF);
+
+  return p4est_face_corners[nf][nfc];
+}
+
+size_t
+p4est_connectivity_memory_used (p4est_connectivity_t * conn)
+{
+  return sizeof (p4est_connectivity_t) +
+    (conn->num_vertices > 0 ?
+     (conn->num_vertices * 3 * sizeof (double) +
+      conn->num_trees * P4EST_CHILDREN * sizeof (p4est_topidx_t)) : 0) +
+    conn->num_trees * P4EST_FACES * (sizeof (p4est_topidx_t) +
+                                     sizeof (int8_t)) +
+#ifdef P4_TO_P8
+    conn->num_trees * P8EST_EDGES * sizeof (p4est_topidx_t) +
+    (conn->num_edges + 1) * sizeof (p4est_topidx_t) +
+    conn->ett_offset[conn->num_edges] * (sizeof (p4est_topidx_t) +
+                                         sizeof (int8_t)) +
+#endif
+    conn->num_trees * P4EST_CHILDREN * sizeof (p4est_topidx_t) +
+    (conn->num_corners + 1) * sizeof (p4est_topidx_t) +
+    conn->ctt_offset[conn->num_corners] * (sizeof (p4est_topidx_t) +
+                                           sizeof (int8_t));
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new_copy (p4est_topidx_t num_vertices,
+                             p4est_topidx_t num_trees,
+#ifdef P4_TO_P8
+                             p4est_topidx_t num_edges,
+#endif
+                             p4est_topidx_t num_corners,
+                             const double *vertices,
+                             const p4est_topidx_t * ttv,
+                             const p4est_topidx_t * ttt, const int8_t * ttf,
+#ifdef P4_TO_P8
+                             const p4est_topidx_t * tte,
+                             const p4est_topidx_t * eoff,
+                             const p4est_topidx_t * ett, const int8_t * ete,
+#endif
+                             const p4est_topidx_t * ttc,
+                             const p4est_topidx_t * coff,
+                             const p4est_topidx_t * ctt, const int8_t * ctc)
+{
+#ifdef P4_TO_P8
+  p4est_topidx_t      num_ett;
+#endif
+  p4est_topidx_t      num_ctt;
+  p4est_connectivity_t *conn;
+
+#ifdef P4_TO_P8
+  num_ett = eoff[num_edges];
+#endif
+  num_ctt = coff[num_corners];
+  conn = p4est_connectivity_new (num_vertices, num_trees,
+#ifdef P4_TO_P8
+                                 num_edges, num_ett,
+#endif
+                                 num_corners, num_ctt);
+
+  if (num_vertices > 0) {
+    P4EST_ASSERT (vertices != NULL && ttv != NULL);
+    memcpy (conn->vertices, vertices, sizeof (double) * 3 * num_vertices);
+    memcpy (conn->tree_to_vertex, ttv,
+            sizeof (p4est_topidx_t) * P4EST_CHILDREN * num_trees);
+  }
+  else {
+    conn->vertices = NULL;
+    conn->tree_to_vertex = NULL;
+  }
+  memcpy (conn->tree_to_tree, ttt,
+          sizeof (p4est_topidx_t) * P4EST_FACES * num_trees);
+  memcpy (conn->tree_to_face, ttf, sizeof (int8_t) * P4EST_FACES * num_trees);
+
+#ifdef P4_TO_P8
+  if (num_edges > 0) {
+    memcpy (conn->tree_to_edge, tte,
+            sizeof (p4est_topidx_t) * P8EST_EDGES * num_trees);
+    memcpy (conn->edge_to_tree, ett, sizeof (p4est_topidx_t) * num_ett);
+    memcpy (conn->edge_to_edge, ete, sizeof (int8_t) * num_ett);
+  }
+  memcpy (conn->ett_offset, eoff, sizeof (p4est_topidx_t) * (num_edges + 1));
+#endif
+
+  if (num_corners > 0) {
+    memcpy (conn->tree_to_corner, ttc,
+            sizeof (p4est_topidx_t) * P4EST_CHILDREN * num_trees);
+    memcpy (conn->corner_to_tree, ctt, sizeof (p4est_topidx_t) * num_ctt);
+    memcpy (conn->corner_to_corner, ctc, sizeof (int8_t) * num_ctt);
+  }
+  memcpy (conn->ctt_offset, coff,
+          sizeof (p4est_topidx_t) * (num_corners + 1));
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+
+  return conn;
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new (p4est_topidx_t num_vertices, p4est_topidx_t num_trees,
+#ifdef P4_TO_P8
+                        p4est_topidx_t num_edges, p4est_topidx_t num_ett,
+#endif
+                        p4est_topidx_t num_corners, p4est_topidx_t num_ctt)
+{
+  p4est_connectivity_t *conn;
+
+  conn = P4EST_ALLOC_ZERO (p4est_connectivity_t, 1);
+
+  conn->num_vertices = num_vertices;
+  conn->num_trees = num_trees;
+  if (num_vertices > 0) {
+    conn->vertices = P4EST_ALLOC (double, 3 * num_vertices);
+    conn->tree_to_vertex =
+      P4EST_ALLOC (p4est_topidx_t, P4EST_CHILDREN * num_trees);
+  }
+  else {
+    conn->vertices = NULL;
+    conn->tree_to_vertex = NULL;
+  }
+  conn->tree_to_tree = P4EST_ALLOC (p4est_topidx_t, P4EST_FACES * num_trees);
+  conn->tree_to_face = P4EST_ALLOC (int8_t, P4EST_FACES * num_trees);
+
+#ifdef P4_TO_P8
+  conn->num_edges = num_edges;
+  if (num_edges > 0) {
+    conn->tree_to_edge =
+      P4EST_ALLOC (p4est_topidx_t, P8EST_EDGES * num_trees);
+    conn->edge_to_tree = P4EST_ALLOC (p4est_topidx_t, num_ett);
+    conn->edge_to_edge = P4EST_ALLOC (int8_t, num_ett);
+  }
+  else {
+    conn->tree_to_edge = NULL;
+    conn->edge_to_tree = NULL;
+    conn->edge_to_edge = NULL;
+  }
+  conn->ett_offset = P4EST_ALLOC (p4est_topidx_t, num_edges + 1);
+  conn->ett_offset[num_edges] = num_ett;
+#endif
+
+  conn->num_corners = num_corners;
+  if (num_corners > 0) {
+    conn->tree_to_corner =
+      P4EST_ALLOC (p4est_topidx_t, P4EST_CHILDREN * num_trees);
+    conn->corner_to_tree = P4EST_ALLOC (p4est_topidx_t, num_ctt);
+    conn->corner_to_corner = P4EST_ALLOC (int8_t, num_ctt);
+  }
+  else {
+    conn->tree_to_corner = NULL;
+    conn->corner_to_tree = NULL;
+    conn->corner_to_corner = NULL;
+  }
+  conn->ctt_offset = P4EST_ALLOC (p4est_topidx_t, num_corners + 1);
+  conn->ctt_offset[num_corners] = num_ctt;
+
+  return conn;
+}
+
+void
+p4est_connectivity_destroy (p4est_connectivity_t * conn)
+{
+  P4EST_FREE (conn->vertices);
+  P4EST_FREE (conn->tree_to_vertex);
+
+  P4EST_FREE (conn->tree_to_tree);
+  P4EST_FREE (conn->tree_to_face);
+
+#ifdef P4_TO_P8
+  P4EST_FREE (conn->tree_to_edge);
+  P4EST_FREE (conn->ett_offset);
+  P4EST_FREE (conn->edge_to_tree);
+  P4EST_FREE (conn->edge_to_edge);
+#endif
+
+  P4EST_FREE (conn->tree_to_corner);
+  P4EST_FREE (conn->ctt_offset);
+  P4EST_FREE (conn->corner_to_tree);
+  P4EST_FREE (conn->corner_to_corner);
+
+  p4est_connectivity_set_attr (conn, 0);
+
+  P4EST_FREE (conn);
+}
+
+void
+p4est_connectivity_set_attr (p4est_connectivity_t * conn,
+                             size_t bytes_per_tree)
+{
+  if (bytes_per_tree > 0) {
+    P4EST_ASSERT (conn->tree_to_attr == NULL);
+    conn->tree_to_attr = P4EST_ALLOC (char, bytes_per_tree * conn->num_trees);
+  }
+  else {
+    P4EST_FREE (conn->tree_to_attr);
+    conn->tree_to_attr = NULL;
+  }
+  conn->tree_attr_bytes = bytes_per_tree;
+}
+
+int
+p4est_connectivity_is_valid (p4est_connectivity_t * conn)
+{
+  int                 nvert;
+  int                 face, rface, nface, orientation;
+  int                 errcode, errcount;
+#ifdef P4_TO_P8
+  int                 edge, nedge;
+  int                 flip, nflip, nflip1, nflip2;
+  p4est_topidx_t      aedge, edge_begin, edge_end;
+  p4est_topidx_t      nett;
+#endif
+  int                 corner, ncorner;
+  int                 good, cfound;
+  p4est_topidx_t      vertex, tree, ntree;
+  p4est_topidx_t      acorner, corner_begin, corner_end;
+  p4est_topidx_t      nctt;
+  const p4est_topidx_t num_vertices = conn->num_vertices;
+  const p4est_topidx_t num_trees = conn->num_trees;
+  const p4est_topidx_t *ttv = conn->tree_to_vertex;
+  const p4est_topidx_t *ttt = conn->tree_to_tree;
+  const int8_t       *ttf = conn->tree_to_face;
+#ifdef P4_TO_P8
+  const p4est_topidx_t num_edges = conn->num_edges;
+  const p4est_topidx_t *tte = conn->tree_to_edge;
+  const p4est_topidx_t *eoff = conn->ett_offset;
+  const p4est_topidx_t *ett = conn->edge_to_tree;
+  const int8_t       *ete = conn->edge_to_edge;
+  const p4est_topidx_t num_ett = eoff[num_edges];
+  p8est_edge_info_t   ei;
+  sc_array_t         *eta = &ei.edge_transforms;
+#endif
+  const p4est_topidx_t num_corners = conn->num_corners;
+  const p4est_topidx_t *ttc = conn->tree_to_corner;
+  const p4est_topidx_t *coff = conn->ctt_offset;
+  const p4est_topidx_t *ctt = conn->corner_to_tree;
+  const int8_t       *ctc = conn->corner_to_corner;
+  const p4est_topidx_t num_ctt = coff[num_corners];
+  p4est_corner_info_t ci;
+  sc_array_t         *cta = &ci.corner_transforms;
+
+  good = 0;
+#ifdef P4_TO_P8
+  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+#endif
+  sc_array_init (cta, sizeof (p4est_corner_transform_t));
+
+  if (num_vertices == 0 && (conn->vertices != NULL || ttv != NULL)) {
+    P4EST_NOTICE ("Zero vertices still with arrays\n");
+    goto failure;
+  }
+  if (num_vertices > 0 && (conn->vertices == NULL || ttv == NULL)) {
+    P4EST_NOTICE ("Nonzero vertices missing arrays\n");
+    goto failure;
+  }
+
+#ifdef P4_TO_P8
+  for (nett = 0; nett < num_ett; ++nett) {
+    if (ett[nett] < 0 || ett[nett] >= num_trees) {
+      P4EST_NOTICEF ("Edge to tree %lld out of range\n", (long long) nett);
+      goto failure;
+    }
+    if (ete[nett] < 0 || ete[nett] >= 24) {
+      P4EST_NOTICEF ("Edge to edge %lld out of range\n", (long long) nett);
+      goto failure;
+    }
+  }
+#endif
+
+  for (nctt = 0; nctt < num_ctt; ++nctt) {
+    if (ctt[nctt] < 0 || ctt[nctt] >= num_trees) {
+      P4EST_NOTICEF ("Corner to tree %lld out of range\n", (long long) nctt);
+      goto failure;
+    }
+    if (ctc[nctt] < 0 || ctc[nctt] >= P4EST_CHILDREN) {
+      P4EST_NOTICEF ("Corner to corner %lld out of range\n",
+                     (long long) nctt);
+      goto failure;
+    }
+  }
+
+  if (num_vertices > 0) {
+    for (tree = 0; tree < num_trees; ++tree) {
+      for (nvert = 0; nvert < P4EST_CHILDREN; ++nvert) {
+        vertex = ttv[tree * P4EST_CHILDREN + nvert];
+        if (vertex < 0 || vertex >= num_vertices) {
+          P4EST_NOTICEF ("Tree to vertex out of range %lld %d",
+                         (long long) tree, nvert);
+          goto failure;
+        }
+      }
+    }
+  }
+
+  if ((conn->tree_to_attr != NULL) != (conn->tree_attr_bytes > 0)) {
+    P4EST_NOTICEF ("Tree attribute properties inconsistent %lld",
+                   (long long) conn->tree_attr_bytes);
+    goto failure;
+  }
+
+  for (tree = 0; tree < num_trees; ++tree) {
+    for (face = 0; face < P4EST_FACES; ++face) {
+      ntree = ttt[tree * P4EST_FACES + face];
+      if (ntree < 0 || ntree >= num_trees) {
+        P4EST_NOTICEF ("Tree to tree out of range %lld %d\n",
+                       (long long) tree, face);
+        goto failure;
+      }
+      rface = (int) ttf[tree * P4EST_FACES + face];
+      if (rface < 0 || rface >= P4EST_FACES * P4EST_HALF) {
+        P4EST_NOTICEF ("Tree to face out of range %lld %d\n",
+                       (long long) tree, face);
+        goto failure;
+      }
+      nface = rface % P4EST_FACES;      /* clamp to a real face index */
+      orientation = rface / P4EST_FACES;        /* 0..P4EST_HALF-1 */
+      if (ntree == tree) {
+        /* no neighbor across this face or self-periodic */
+        if (nface == face && orientation != 0) {
+          P4EST_NOTICEF ("Face invalid in %lld %d\n", (long long) tree, face);
+          goto failure;
+        }
+      }
+      if (ntree != tree || nface != face) {
+        /* check reciprocity */
+        if (ttt[ntree * P4EST_FACES + nface] != tree) {
+          P4EST_NOTICEF ("Tree to tree reciprocity in %lld %d\n",
+                         (long long) tree, face);
+          goto failure;
+        }
+        if ((int) ttf[ntree * P4EST_FACES + nface] !=
+            face + P4EST_FACES * orientation) {
+          P4EST_NOTICEF ("Tree to face reciprocity in %lld %d\n",
+                         (long long) tree, face);
+          goto failure;
+        }
+      }
+    }
+
+#ifdef P4_TO_P8
+    for (aedge = 0; aedge < num_edges; ++aedge) {
+      if (eoff[aedge + 1] < eoff[aedge]) {
+        P4EST_NOTICEF ("Edge offset backwards %lld\n", (long long) aedge);
+        goto failure;
+      }
+    }
+    if (num_edges > 0) {
+      for (edge = 0; edge < P8EST_EDGES; ++edge) {
+        p8est_find_edge_transform (conn, tree, edge, &ei);
+        aedge = tte[tree * P8EST_EDGES + edge];
+        if (aedge < -1 || aedge >= num_edges) {
+          P4EST_NOTICEF ("Tree to edge out of range %lld %d\n",
+                         (long long) tree, edge);
+          goto failure;
+        }
+        if (aedge == -1) {
+          continue;
+        }
+        errcode = errcount = 0;
+        flip = nflip1 = nflip2 = -1;
+        edge_begin = eoff[aedge];
+        edge_end = eoff[aedge + 1];
+        if (edge_begin < 0 || edge_begin >= num_ett ||
+            edge_end < 0 || edge_end > num_ett) {
+          P4EST_NOTICEF ("Invalid edge range %lld %d\n",
+                         (long long) tree, edge);
+          goto failure;
+        }
+        for (nett = edge_begin; nett < edge_end; ++nett) {
+          ntree = ett[nett];
+          nedge = (int) ete[nett] % P8EST_EDGES;
+          if (tte[ntree * P8EST_EDGES + nedge] != aedge) {
+            P4EST_NOTICEF ("Edge to edge reciprocity in %lld %d %lld\n",
+                           (long long) tree, edge, (long long) nett);
+            goto failure;
+          }
+          nflip = (int) ete[nett] / P8EST_EDGES;
+          if (ntree == tree && nedge == edge) {
+            if (flip != -1) {
+              errcode = 1;
+              break;
+            }
+            flip = nflip;
+            continue;
+          }
+          ++errcount;
+        }
+        if (errcode > 0) {
+          P4EST_NOTICEF ("Shared edge %lld %d %lld inconsistency %d\n",
+                         (long long) tree, edge, (long long) nett, errcode);
+          goto failure;
+        }
+        if (flip == -1 ||
+            !(nflip1 == -1 || nflip1 == flip) ||
+            !(nflip2 == -1 || nflip2 == flip)) {
+          P4EST_NOTICEF ("Shared edge %lld %d inconsistent flip\n",
+                         (long long) tree, edge);
+          goto failure;
+        }
+      }
+    }
+#endif
+
+    for (acorner = 0; acorner < num_corners; ++acorner) {
+      if (coff[acorner + 1] < coff[acorner]) {
+        P4EST_NOTICEF ("Corner offset backwards %lld\n", (long long) acorner);
+        goto failure;
+      }
+    }
+    if (num_corners > 0) {
+      for (corner = 0; corner < P4EST_CHILDREN; ++corner) {
+        p4est_find_corner_transform (conn, tree, corner, &ci);
+        acorner = ttc[tree * P4EST_CHILDREN + corner];
+        if (acorner < -1 || acorner >= num_corners) {
+          P4EST_NOTICEF ("Tree to corner out of range %lld %d\n",
+                         (long long) tree, corner);
+          goto failure;
+        }
+        if (acorner == -1) {
+          continue;
+        }
+        errcode = errcount = 0;
+        cfound = 0;
+        corner_begin = coff[acorner];
+        corner_end = coff[acorner + 1];
+        if (corner_begin < 0 || corner_begin >= num_ctt ||
+            corner_end < 0 || corner_end > num_ctt) {
+          P4EST_NOTICEF ("Invalid corner range %lld %d\n",
+                         (long long) tree, corner);
+          goto failure;
+        }
+        for (nctt = corner_begin; nctt < corner_end; ++nctt) {
+          ntree = ctt[nctt];
+          ncorner = (int) ctc[nctt];
+          if (ttc[ntree * P4EST_CHILDREN + ncorner] != acorner) {
+            P4EST_NOTICEF ("Corner to corner reciprocity in %lld %d %lld\n",
+                           (long long) tree, corner, (long long) nctt);
+            goto failure;
+          }
+          if (ntree == tree && ncorner == corner) {
+            if (cfound) {
+              errcode = 1;
+              break;
+            }
+            cfound = 1;
+            continue;
+          }
+          ++errcount;
+        }
+        if (errcode > 0) {
+          P4EST_NOTICEF ("Shared corner %lld %d %lld inconsistency %d\n",
+                         (long long) tree, corner, (long long) nctt, errcode);
+          goto failure;
+        }
+        if (!cfound) {
+          P4EST_NOTICEF ("Shared corner %lld %d inconsistent count B\n",
+                         (long long) tree, corner);
+          goto failure;
+        }
+      }
+    }
+  }
+  good = 1;
+
+failure:
+#ifdef P4_TO_P8
+  sc_array_reset (eta);
+#endif
+  sc_array_reset (cta);
+
+  return good;
+}
+
+int
+p4est_connectivity_is_equal (p4est_connectivity_t * conn1,
+                             p4est_connectivity_t * conn2)
+{
+  size_t              topsize, int8size;
+  size_t              tcount;
+  p4est_topidx_t      num_vertices;
+#ifdef P4_TO_P8
+  p4est_topidx_t      num_edges, num_ett;
+#endif
+  p4est_topidx_t      num_corners, num_ctt;
+
+  topsize = sizeof (p4est_topidx_t);
+  int8size = sizeof (int8_t);
+
+  if (conn1->num_vertices != conn2->num_vertices ||
+      conn1->num_trees != conn2->num_trees ||
+#ifdef P4_TO_P8
+      conn1->num_edges != conn2->num_edges ||
+#endif
+      conn1->num_corners != conn2->num_corners) {
+    return 0;
+  }
+
+  num_vertices = conn1->num_vertices;
+  if (num_vertices > 0) {
+    P4EST_ASSERT (conn1->vertices != NULL && conn2->vertices != NULL);
+    if (memcmp (conn1->vertices, conn2->vertices,
+                sizeof (double) * 3 * num_vertices)) {
+      return 0;
+    }
+
+    P4EST_ASSERT (conn1->tree_to_vertex != NULL &&
+                  conn2->tree_to_vertex != NULL);
+    tcount = (size_t) (P4EST_CHILDREN * conn1->num_trees);
+    if (memcmp (conn1->tree_to_vertex, conn2->tree_to_vertex,
+                tcount * topsize)) {
+      return 0;
+    }
+  }
+
+#ifdef P4_TO_P8
+  tcount = (size_t) (P8EST_EDGES * conn1->num_trees);
+  if (conn1->num_edges > 0 &&
+      memcmp (conn1->tree_to_edge, conn2->tree_to_edge, tcount * topsize)) {
+    return 0;
+  }
+#endif
+
+  tcount = (size_t) (P4EST_CHILDREN * conn1->num_trees);
+  if (conn1->num_corners > 0 &&
+      memcmp (conn1->tree_to_corner, conn2->tree_to_corner,
+              tcount * topsize)) {
+    return 0;
+  }
+
+  tcount = (size_t) (P4EST_FACES * conn1->num_trees);
+  if (memcmp (conn1->tree_to_tree, conn2->tree_to_tree, tcount * topsize) ||
+      memcmp (conn1->tree_to_face, conn2->tree_to_face, tcount * int8size)) {
+    return 0;
+  }
+
+  if ((conn1->tree_to_attr == NULL) != (conn2->tree_to_attr == NULL) ||
+      conn1->tree_attr_bytes != conn2->tree_attr_bytes) {
+    return 0;
+  }
+  tcount = (size_t) conn1->num_trees;
+  if (conn1->tree_to_attr != NULL &&
+      memcmp (conn1->tree_to_attr, conn2->tree_to_attr,
+              tcount * conn1->tree_attr_bytes)) {
+    return 0;
+  }
+
+#ifdef P4_TO_P8
+  num_edges = conn1->num_edges;
+  num_ett = conn1->ett_offset[num_edges];
+  /* when there are no edges, the latter two ranges are zero */
+  if (memcmp (conn1->ett_offset, conn2->ett_offset,
+              topsize * (num_edges + 1)) ||
+      memcmp (conn1->edge_to_tree, conn2->edge_to_tree,
+              topsize * num_ett) ||
+      memcmp (conn1->edge_to_edge, conn2->edge_to_edge, int8size * num_ett)) {
+    return 0;
+  }
+#endif
+
+  num_corners = conn1->num_corners;
+  num_ctt = conn1->ctt_offset[num_corners];
+  /* when there are no corners, the latter two ranges are zero */
+  if (memcmp (conn1->ctt_offset, conn2->ctt_offset,
+              topsize * (num_corners + 1)) ||
+      memcmp (conn1->corner_to_tree, conn2->corner_to_tree,
+              topsize * num_ctt) ||
+      memcmp (conn1->corner_to_corner, conn2->corner_to_corner,
+              int8size * num_ctt)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int
+p4est_connectivity_sink (p4est_connectivity_t * conn, sc_io_sink_t * sink)
+{
+  int                 retval;
+  int                 has_tree_attr;
+  char                magic8[8 + 1];
+  char                pkgversion24[24 + 1];
+  size_t              tree_attr_bytes;
+  size_t              u64z, topsize, int8size;
+  size_t              tcount;
+  uint64_t            array10[10];
+  p4est_topidx_t      num_vertices, num_trees;
+  p4est_topidx_t      num_edges, num_ett, num_corners, num_ctt;
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+
+  retval = 0;
+  num_vertices = conn->num_vertices;
+  num_trees = conn->num_trees;
+#ifdef P4_TO_P8
+  num_edges = conn->num_edges;
+  num_ett = conn->ett_offset[num_edges];
+#else
+  num_edges = num_ett = 0;
+#endif
+  num_corners = conn->num_corners;
+  num_ctt = conn->ctt_offset[num_corners];
+  has_tree_attr = ((tree_attr_bytes = conn->tree_attr_bytes) > 0);
+
+  strncpy (magic8, P4EST_STRING, 8);
+  magic8[8] = '\0';
+  retval = retval || sc_io_sink_write (sink, magic8, 8);
+
+  strncpy (pkgversion24, P4EST_PACKAGE_VERSION, 24);
+  pkgversion24[24] = '\0';
+  retval = retval || sc_io_sink_write (sink, pkgversion24, 24);
+
+  u64z = sizeof (uint64_t);
+  topsize = sizeof (p4est_topidx_t);
+  int8size = sizeof (int8_t);
+  array10[0] = P4EST_ONDISK_FORMAT;
+  array10[1] = (uint64_t) topsize;
+  array10[2] = (uint64_t) num_vertices;
+  array10[3] = (uint64_t) num_trees;
+  array10[4] = (uint64_t) num_edges;
+  array10[5] = (uint64_t) num_ett;
+  array10[6] = (uint64_t) num_corners;
+  array10[7] = (uint64_t) num_ctt;
+  array10[8] = (uint64_t) conn->tree_attr_bytes;
+  array10[9] = (uint64_t) 0;
+  retval = retval || sc_io_sink_write (sink, array10, 10 * u64z);
+
+  if (num_vertices > 0) {
+    tcount = (size_t) (3 * num_vertices);
+    retval = retval ||
+      sc_io_sink_write (sink, conn->vertices, tcount * sizeof (double));
+  }
+
+#ifdef P4_TO_P8
+  if (num_edges > 0) {
+    tcount = (size_t) (P8EST_EDGES * num_trees);
+    retval = retval ||
+      sc_io_sink_write (sink, conn->tree_to_edge, tcount * topsize);
+  }
+#endif
+
+  tcount = (size_t) (P4EST_CHILDREN * num_trees);
+  if (num_vertices > 0) {
+    retval = retval ||
+      sc_io_sink_write (sink, conn->tree_to_vertex, tcount * topsize);
+  }
+  if (num_corners > 0) {
+    retval = retval ||
+      sc_io_sink_write (sink, conn->tree_to_corner, tcount * topsize);
+  }
+
+  tcount = (size_t) (P4EST_FACES * num_trees);
+  retval = retval ||
+    sc_io_sink_write (sink, conn->tree_to_tree, tcount * topsize) ||
+    sc_io_sink_write (sink, conn->tree_to_face, tcount * int8size);
+
+  if (has_tree_attr) {
+    tcount = (size_t) num_trees;
+    retval = retval ||
+      sc_io_sink_write (sink, conn->tree_to_attr, tcount * tree_attr_bytes);
+  }
+
+#ifdef P4_TO_P8
+  retval = retval || sc_io_sink_write (sink, conn->ett_offset,
+                                       topsize * (num_edges + 1));
+  if (num_edges > 0) {
+    retval = retval ||
+      sc_io_sink_write (sink, conn->edge_to_tree, topsize * num_ett) ||
+      sc_io_sink_write (sink, conn->edge_to_edge, int8size * num_ett);
+  }
+#endif
+
+  retval = retval || sc_io_sink_write (sink, conn->ctt_offset,
+                                       topsize * (num_corners + 1));
+  if (num_corners > 0) {
+    retval = retval ||
+      sc_io_sink_write (sink, conn->corner_to_tree, topsize * num_ctt) ||
+      sc_io_sink_write (sink, conn->corner_to_corner, int8size * num_ctt);
+  }
+
+  return retval;
+}
+
+sc_array_t         *
+p4est_connectivity_deflate (p4est_connectivity_t * conn,
+                            p4est_connectivity_encode_t code)
+{
+  int                 retval;
+  sc_array_t         *buffer;
+  sc_io_sink_t       *sink;
+
+  buffer = sc_array_new (sizeof (char));
+
+  /* This sink writes to a memory buffer so no file errors are caught. */
+  sink = sc_io_sink_new (SC_IO_TYPE_BUFFER, SC_IO_MODE_WRITE,
+                         SC_IO_ENCODE_NONE, buffer);
+  SC_CHECK_ABORT (sink != NULL, "sink open from buffer");
+
+  retval = p4est_connectivity_sink (conn, sink);
+  SC_CHECK_ABORT (retval == 0, "sink connectivity");
+
+  retval = sc_io_sink_destroy (sink);
+  SC_CHECK_ABORT (retval == 0, "destroy sink");
+
+  return buffer;
+}
+
+int
+p4est_connectivity_save (const char *filename, p4est_connectivity_t * conn)
+{
+  int                 retval;
+  sc_io_sink_t       *sink;
+
+  sink = sc_io_sink_new (SC_IO_TYPE_FILENAME, SC_IO_MODE_WRITE,
+                         SC_IO_ENCODE_NONE, filename);
+  if (sink == NULL) {
+    return -1;
+  }
+
+  /* Close file even on earlier write error */
+  retval = p4est_connectivity_sink (conn, sink);
+  retval = sc_io_sink_destroy (sink) || retval;
+
+  return retval;
+}
+
+p4est_connectivity_t *
+p4est_connectivity_source (sc_io_source_t * source)
+{
+  int                 retval;
+  int                 has_tree_attr;
+  char                magic8[9];
+  char                pkgversion24[25];
+  size_t              tree_attr_bytes;
+  size_t              u64z, topsize, int8size;
+  size_t              tcount;
+  uint64_t            array10[10];
+  p4est_topidx_t      num_vertices, num_trees;
+  p4est_topidx_t      num_edges, num_ett, num_corners, num_ctt;
+  p4est_connectivity_t *conn = NULL;
+
+  retval = sc_io_source_read (source, magic8, 8, NULL);
+  magic8[8] = '\0';
+  if (retval || strncmp (magic8, P4EST_STRING, 8)) {
+    /* "invalid magic" */
+    return NULL;
+  }
+  retval = sc_io_source_read (source, pkgversion24, 24, NULL);
+  pkgversion24[24] = '\0';
+  if (retval) {
+    /* "read package version" */
+    return NULL;
+  }
+
+  u64z = sizeof (uint64_t);
+  topsize = sizeof (p4est_topidx_t);
+  int8size = sizeof (int8_t);
+  retval = sc_io_source_read (source, array10, 10 * u64z, NULL);
+  if (retval) {
+    /*"read header" */
+    return NULL;
+  }
+  if (array10[0] != P4EST_ONDISK_FORMAT) {
+    /* "on-disk format mismatch" */
+    return NULL;
+  }
+  if (array10[1] != (uint64_t) topsize) {
+    /* "p4est_topidx_t size mismatch" */
+    return NULL;
+  }
+  num_vertices = (p4est_topidx_t) array10[2];
+  num_trees = (p4est_topidx_t) array10[3];
+  num_edges = (p4est_topidx_t) array10[4];
+  num_ett = (p4est_topidx_t) array10[5];
+  num_corners = (p4est_topidx_t) array10[6];
+  num_ctt = (p4est_topidx_t) array10[7];
+  has_tree_attr = ((tree_attr_bytes = (size_t) array10[8]) > 0);
+  if (num_vertices < 0) {
+    /* "negative num_vertices" */
+    return NULL;
+  }
+  if (num_trees < 0) {
+    /* "negative num_trees" */
+    return NULL;
+  }
+#ifdef P4_TO_P8
+  if (num_edges < 0) {
+    /* "negative num_edges" */
+    return NULL;
+  }
+  if (num_ett < 0) {
+    /* "negative num_ett" */
+    return NULL;
+  }
+#else
+  if (num_edges != 0) {
+    /* "num_edges must be zero in 2D" */
+    return NULL;
+  }
+  if (num_ett != 0) {
+    /* "num_ett must be zero in 2D" */
+    return NULL;
+  }
+#endif
+  if (num_corners < 0) {
+    /* "negative num_corners" */
+    return NULL;
+  }
+  if (num_ctt < 0) {
+    /* "negative num_ctt" */
+    return NULL;
+  }
+
+  conn = p4est_connectivity_new (num_vertices, num_trees,
+#ifdef P4_TO_P8
+                                 num_edges, num_ett,
+#endif
+                                 num_corners, num_ctt);
+  p4est_connectivity_set_attr (conn, tree_attr_bytes);
+
+  if (num_vertices > 0) {
+    tcount = (size_t) (3 * num_vertices);
+    retval = sc_io_source_read (source, conn->vertices,
+                                tcount * sizeof (double), NULL);
+    if (retval) {
+      /* "read vertices" */
+      p4est_connectivity_destroy (conn);
+      return NULL;
+    }
+  }
+
+#ifdef P4_TO_P8
+  if (num_edges > 0) {
+    tcount = (size_t) (P8EST_EDGES * num_trees);
+    retval = sc_io_source_read (source, conn->tree_to_edge, topsize * tcount,
+                                NULL);
+    if (retval) {
+      /* "read tte" */
+      p4est_connectivity_destroy (conn);
+      return NULL;
+    }
+  }
+#endif
+
+  tcount = (size_t) (P4EST_CHILDREN * num_trees);
+  if (num_vertices > 0) {
+    retval = sc_io_source_read (source, conn->tree_to_vertex,
+                                topsize * tcount, NULL);
+    if (retval) {
+      /* "read ttv" */
+      p4est_connectivity_destroy (conn);
+      return NULL;
+    }
+  }
+  if (num_corners > 0) {
+    retval = sc_io_source_read (source, conn->tree_to_corner,
+                                topsize * tcount, NULL);
+    if (retval) {
+      /* "read ttc" */
+      p4est_connectivity_destroy (conn);
+      return NULL;
+    }
+  }
+  tcount = (size_t) (P4EST_FACES * num_trees);
+  retval = sc_io_source_read (source, conn->tree_to_tree, topsize * tcount,
+                              NULL);
+  if (retval) {
+    /* "read ttt" */
+    p4est_connectivity_destroy (conn);
+    return NULL;
+  }
+  retval = sc_io_source_read (source, conn->tree_to_face, int8size * tcount,
+                              NULL);
+  if (retval) {
+    /* "read ttf" */
+    p4est_connectivity_destroy (conn);
+    return NULL;
+  }
+  if (has_tree_attr) {
+    tcount = (size_t) num_trees;
+    retval = sc_io_source_read (source, conn->tree_to_attr,
+                                tcount * tree_attr_bytes, NULL);
+    if (retval) {
+      /* "write tree_to_attr" */
+      p4est_connectivity_destroy (conn);
+      return NULL;
+    }
+  }
+
+#ifdef P4_TO_P8
+  retval = sc_io_source_read (source, conn->ett_offset,
+                              topsize * (num_edges + 1), NULL);
+  if (retval || num_ett != conn->ett_offset[num_edges]) {
+    /* "read ett_offset" */
+    p4est_connectivity_destroy (conn);
+    return NULL;
+  }
+  if (num_edges > 0) {
+    retval = sc_io_source_read (source, conn->edge_to_tree, topsize * num_ett,
+                                NULL);
+    if (retval) {
+      /* "read ett" */
+      p4est_connectivity_destroy (conn);
+      return NULL;
+    }
+    retval = sc_io_source_read (source, conn->edge_to_edge,
+                                int8size * num_ett, NULL);
+    if (retval) {
+      /* "read ete" */
+      p4est_connectivity_destroy (conn);
+      return NULL;
+    }
+  }
+#endif
+
+  retval = sc_io_source_read (source, conn->ctt_offset,
+                              topsize * (num_corners + 1), NULL);
+  if (retval || num_ctt != conn->ctt_offset[num_corners]) {
+    /* "read ctt_offset" */
+    p4est_connectivity_destroy (conn);
+    return NULL;
+  }
+  if (num_corners > 0) {
+    retval = sc_io_source_read (source, conn->corner_to_tree,
+                                topsize * num_ctt, NULL);
+    if (retval) {
+      /* "read ctt" */
+      p4est_connectivity_destroy (conn);
+      return NULL;
+    }
+    retval = sc_io_source_read (source, conn->corner_to_corner,
+                                int8size * num_ctt, NULL);
+    if (retval) {
+      /* "read ctc" */
+      p4est_connectivity_destroy (conn);
+      return NULL;
+    }
+  }
+
+  if (!p4est_connectivity_is_valid (conn)) {
+    /* "invalid connectivity" */
+    p4est_connectivity_destroy (conn);
+    return NULL;
+  }
+
+  return conn;
+}
+
+p4est_connectivity_t *
+p4est_connectivity_inflate (sc_array_t * buffer)
+{
+  int                 retval;
+  p4est_connectivity_t *conn;
+  sc_io_source_t     *source;
+
+  /* This source reads from a memory buffer so no file errors are caught. */
+  source = sc_io_source_new (SC_IO_TYPE_BUFFER, SC_IO_ENCODE_NONE, buffer);
+  SC_CHECK_ABORT (source != NULL, "source open from buffer");
+
+  conn = p4est_connectivity_source (source);
+  SC_CHECK_ABORT (conn != NULL, "source connectivity");
+
+  retval = sc_io_source_destroy (source);
+  SC_CHECK_ABORT (retval == 0, "destroy source");
+
+  return conn;
+}
+
+p4est_connectivity_t *
+p4est_connectivity_load (const char *filename, size_t * bytes)
+{
+  int                 retval;
+  size_t              bytes_in;
+  sc_io_source_t     *source;
+  p4est_connectivity_t *conn;
+
+  source = sc_io_source_new (SC_IO_TYPE_FILENAME,
+                             SC_IO_ENCODE_NONE, filename);
+  if (source == NULL) {
+    return NULL;
+  }
+
+  /* Get byte length and close file even on earlier read error */
+  conn = p4est_connectivity_source (source);
+  retval = sc_io_source_complete (source, &bytes_in, NULL) || conn == NULL;
+  retval = sc_io_source_destroy (source) || retval;
+  if (retval) {
+    if (conn != NULL) {
+      p4est_connectivity_destroy (conn);
+    }
+    return NULL;
+  }
+
+  if (bytes != NULL) {
+    *bytes = bytes_in;
+  }
+  return conn;
+}
+
+#ifndef P4_TO_P8
+
+p4est_connectivity_t *
+p4est_connectivity_new_unitsquare (void)
+{
+  const p4est_topidx_t num_vertices = 4;
+  const p4est_topidx_t num_trees = 1;
+  const p4est_topidx_t num_ctt = 0;
+  const double        vertices[4 * 3] = {
+    0, 0, 0,
+    1, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+  };
+  const p4est_topidx_t tree_to_vertex[1 * 4] = {
+    0, 1, 2, 3,
+  };
+  const p4est_topidx_t tree_to_tree[1 * 4] = {
+    0, 0, 0, 0,
+  };
+  const int8_t        tree_to_face[1 * 4] = {
+    0, 1, 2, 3,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, 0,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      NULL, &num_ctt, NULL, NULL);
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new_periodic (void)
+{
+  const p4est_topidx_t num_vertices = 4;
+  const p4est_topidx_t num_trees = 1;
+  const p4est_topidx_t num_corners = 1;
+  const double        vertices[4 * 3] = {
+    0, 0, 0,
+    1, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+  };
+  const p4est_topidx_t tree_to_vertex[1 * 4] = {
+    0, 1, 2, 3,
+  };
+  const p4est_topidx_t tree_to_tree[1 * 4] = {
+    0, 0, 0, 0,
+  };
+  const int8_t        tree_to_face[1 * 4] = {
+    1, 0, 3, 2,
+  };
+  const p4est_topidx_t tree_to_corner[1 * 4] = {
+    0, 0, 0, 0,
+  };
+  const p4est_topidx_t ctt_offset[1 + 1] = {
+    0, 4,
+  };
+  const p4est_topidx_t corner_to_tree[4] = {
+    0, 0, 0, 0,
+  };
+  const int8_t        corner_to_corner[4] = {
+    0, 1, 2, 3,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, num_corners,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      tree_to_corner, ctt_offset,
+                                      corner_to_tree, corner_to_corner);
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new_rotwrap (void)
+{
+  const p4est_topidx_t num_vertices = 4;
+  const p4est_topidx_t num_trees = 1;
+  const p4est_topidx_t num_corners = 1;
+  const double        vertices[4 * 3] = {
+    0, 0, 0,
+    1, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+  };
+  const p4est_topidx_t tree_to_vertex[1 * 4] = {
+    0, 1, 2, 3,
+  };
+  const p4est_topidx_t tree_to_tree[1 * 4] = {
+    0, 0, 0, 0,
+  };
+  const int8_t        tree_to_face[1 * 4] = {
+    1, 0, 7, 6,
+  };
+  const p4est_topidx_t tree_to_corner[1 * 4] = {
+    0, 0, 0, 0,
+  };
+  const p4est_topidx_t ctt_offset[1 + 1] = {
+    0, 4,
+  };
+  const p4est_topidx_t corner_to_tree[4] = {
+    0, 0, 0, 0,
+  };
+  const int8_t        corner_to_corner[4] = {
+    0, 1, 2, 3,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, num_corners,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      tree_to_corner, ctt_offset,
+                                      corner_to_tree, corner_to_corner);
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new_corner (void)
+{
+  const p4est_topidx_t num_vertices = 7;
+  const p4est_topidx_t num_trees = 3;
+  const p4est_topidx_t num_corners = 1;
+  const double        vertices[7 * 3] = {
+    -1, -1, 0,
+    0, -1, 0,
+    0, 0, 1,
+    1, 0, 1,
+    1, 1, 1,
+    0, 1, 1,
+    -1, 0, 0,
+  };
+  const p4est_topidx_t tree_to_vertex[3 * 4] = {
+    0, 1, 2, 3, 0, 2, 6, 5, 2, 3, 5, 4,
+  };
+  const p4est_topidx_t tree_to_tree[3 * 4] = {
+    1, 0, 0, 2, 1, 2, 0, 1, 1, 2, 0, 2,
+  };
+  const int8_t        tree_to_face[3 * 4] = {
+    2, 1, 2, 2, 0, 0, 0, 3, 1, 1, 3, 3,
+  };
+  const p4est_topidx_t tree_to_corner[3 * 4] = {
+    -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, -1,
+  };
+  const p4est_topidx_t ctt_offset[1 + 1] = {
+    0, 3,
+  };
+  const p4est_topidx_t corner_to_tree[3] = {
+    0, 1, 2,
+  };
+  const int8_t        corner_to_corner[3] = {
+    2, 1, 0,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, num_corners,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      tree_to_corner, ctt_offset,
+                                      corner_to_tree, corner_to_corner);
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new_pillow (void)
+{
+  const p4est_topidx_t num_vertices = 4;
+  const p4est_topidx_t num_trees = 2;
+  const p4est_topidx_t num_ctt = 0;
+  const double        vertices[4 * 3] = {
+    0, 0, 0,
+    0, 1, 0,
+    1, 0, 0,
+    1, 1, 0,
+  };
+  const p4est_topidx_t tree_to_vertex[2 * 4] = {
+    0, 1, 2, 3, 0, 1, 2, 3,
+  };
+  const p4est_topidx_t tree_to_tree[2 * 4] = {
+    1, 1, 1, 1, 0, 0, 0, 0,
+  };
+  const int8_t        tree_to_face[2 * 4] = {
+    0, 1, 2, 3, 0, 1, 2, 3,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, 0,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      NULL, &num_ctt, NULL, NULL);
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new_moebius (void)
+{
+  const p4est_topidx_t num_vertices = 10;
+  const p4est_topidx_t num_trees = 5;
+  const p4est_topidx_t num_ctt = 0;
+  const double        halfsqrt3 = .5 * sqrt (3.);
+  const double        vertices[10 * 3] = {
+    0, 0, 0,
+    0, 1, 0,
+    1, 0, 0,
+    1, 1, 0,
+    1.5, 0, halfsqrt3,
+    1.5, 1, halfsqrt3,
+    .5, .5, 1.5,
+    .5, .5, 2,
+    -.5, 0, halfsqrt3,
+    -.5, 1, halfsqrt3,
+  };
+  const p4est_topidx_t tree_to_vertex[5 * 4] = {
+    0, 2, 1, 3,
+    3, 5, 2, 4,
+    4, 6, 5, 7,
+    6, 7, 9, 8,
+    9, 8, 1, 0,
+  };
+  const p4est_topidx_t tree_to_tree[5 * 4] = {
+    4, 1, 0, 0,
+    0, 2, 1, 1,
+    1, 3, 2, 2,
+    3, 3, 2, 4,
+    4, 4, 3, 0,
+  };
+  const int8_t        tree_to_face[5 * 4] = {
+    7, 4, 2, 3,
+    5, 4, 2, 3,
+    5, 2, 2, 3,
+    0, 1, 1, 2,
+    0, 1, 3, 4,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, 0,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      NULL, &num_ctt, NULL, NULL);
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new_star (void)
+{
+  const p4est_topidx_t num_vertices = 13;
+  const p4est_topidx_t num_trees = 6;
+  const p4est_topidx_t num_corners = 1;
+  const p4est_topidx_t tree_to_vertex[6 * 4] = {
+    0, 1, 3, 2,
+    0, 3, 5, 4,
+    5, 6, 0, 7,
+    8, 7, 9, 0,
+    9, 0, 10, 11,
+    12, 1, 11, 0,
+  };
+  const p4est_topidx_t tree_to_tree[6 * 4] = {
+    1, 0, 5, 0,
+    2, 1, 0, 1,
+    1, 2, 2, 3,
+    3, 2, 3, 4,
+    4, 5, 3, 4,
+    5, 0, 5, 4,
+  };
+  const int8_t        tree_to_face[6 * 4] = {
+    2, 1, 5, 3,
+    4, 1, 0, 3,
+    4, 1, 2, 5,
+    0, 7, 2, 2,
+    0, 7, 3, 3,
+    0, 6, 2, 5,
+  };
+  const p4est_topidx_t tree_to_corner[6 * 4] = {
+    0, -1, -1, -1, 0, -1, -1, -1, -1, -1, 0, -1,
+    -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, 0,
+  };
+  const p4est_topidx_t ctt_offset[1 + 1] = {
+    0, 6,
+  };
+  const p4est_topidx_t corner_to_tree[6] = {
+    0, 1, 2, 3, 4, 5,
+  };
+  const int8_t        corner_to_corner[6] = {
+    0, 0, 2, 3, 1, 3,
+  };
+  const double        pi = 4.0 * atan (1.0);
+  const double        r1 = 1.;
+  const double        r2 = 1.5;
+  double              vertices[13 * 3];
+  int                 i;
+
+  vertices[0 * 3 + 0] = 0;
+  vertices[0 * 3 + 1] = 0;
+  vertices[0 * 3 + 2] = 0;
+  for (i = 0; i < 6; ++i) {
+    vertices[(2 * i + 1) * 3 + 0] = r1 * cos (i * pi / 3);
+    vertices[(2 * i + 1) * 3 + 1] = r1 * sin (i * pi / 3);
+    vertices[(2 * i + 1) * 3 + 2] = 0;
+    vertices[(2 * i + 2) * 3 + 0] = r2 * cos ((i + .5) * pi / 3);
+    vertices[(2 * i + 2) * 3 + 1] = r2 * sin ((i + .5) * pi / 3);
+    vertices[(2 * i + 2) * 3 + 2] = 0;
+  }
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, num_corners,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      tree_to_corner, ctt_offset,
+                                      corner_to_tree, corner_to_corner);
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new_cubed (void)
+{
+  const p4est_topidx_t num_vertices = 8;
+  const p4est_topidx_t num_trees = 6;
+  const p4est_topidx_t num_ctt = 0;
+  const double        vertices[8 * 3] = {
+    0, 0, 0,
+    1, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+    0, 0, 1,
+    1, 0, 1,
+    0, 1, 1,
+    1, 1, 1,
+  };
+  const p4est_topidx_t tree_to_vertex[6 * 4] = {
+    0, 2, 1, 3,
+    2, 6, 3, 7,
+    0, 4, 2, 6,
+    4, 5, 6, 7,
+    0, 1, 4, 5,
+    1, 3, 5, 7,
+  };
+  const p4est_topidx_t tree_to_tree[6 * 4] = {
+    4, 1, 2, 5,
+    0, 3, 2, 5,
+    0, 3, 4, 1,
+    2, 5, 4, 1,
+    2, 5, 0, 3,
+    4, 1, 0, 3,
+  };
+  const int8_t        tree_to_face[6 * 4] = {
+    2, 0, 0, 2,
+    1, 3, 3, 1,
+    2, 0, 0, 2,
+    1, 3, 3, 1,
+    2, 0, 0, 2,
+    1, 3, 3, 1,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, 0,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      NULL, &num_ctt, NULL, NULL);
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new_disk (void)
+{
+  const p4est_topidx_t num_vertices = 8;
+  const p4est_topidx_t num_trees = 5;
+  const p4est_topidx_t num_ctt = 0;
+  const double        vertices[8 * 3] = {
+    -1, -1, 0,
+    1, -1, 0,
+    -1, 1, 0,
+    1, 1, 0,
+    -3, -3, 0,
+    3, -3, 0,
+    -3, 3, 0,
+    3, 3, 0,
+  };
+  const p4est_topidx_t tree_to_vertex[5 * 4] = {
+    4, 5, 0, 1,
+    4, 0, 6, 2,
+    0, 1, 2, 3,
+    1, 5, 3, 7,
+    2, 3, 6, 7,
+  };
+  const p4est_topidx_t tree_to_tree[5 * 4] = {
+    1, 3, 0, 2,
+    1, 2, 0, 4,
+    1, 3, 0, 4,
+    2, 3, 0, 4,
+    1, 3, 2, 4,
+  };
+  const int8_t        tree_to_face[5 * 4] = {
+    2, 6, 2, 2,
+    0, 0, 0, 4,
+    1, 0, 3, 2,
+    1, 1, 5, 1,
+    7, 3, 3, 3,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, 0,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      NULL, &num_ctt, NULL, NULL);
+}
+
+#endif /* !P4_TO_P8 */
+
+static inline void
+brick_linear_to_xyz (p4est_topidx_t ti, const int logx[P4EST_DIM],
+                     const int rankx[P4EST_DIM], p4est_topidx_t tx[P4EST_DIM])
+{
+  int                 i, j, k;
+  int                 lastlog = 0;
+
+  for (i = 0; i < P4EST_DIM; i++) {
+    tx[i] = 0;
+  }
+
+  for (i = 0; i < P4EST_DIM - 1; i++) {
+    p4est_topidx_t      tempx[3] = { 0, 0, 0 };
+    int                 logi = logx[rankx[i]] - lastlog;
+    int                 idx[3] = { -1, -1, -1 };
+    int                 c = 0;
+
+    for (k = 0; k < P4EST_DIM - i; k++) {
+      int                 d = rankx[i + k];
+
+      idx[d] = 0;
+    }
+    for (k = 0; k < P4EST_DIM; k++) {
+      if (idx[k] == 0) {
+        idx[k] = c++;
+      }
+    }
+
+    for (j = 0; j < logi; j++) {
+      int                 base = (P4EST_DIM - i) * j;
+      int                 shift = (P4EST_DIM - i - 1) * j;
+
+      for (k = 0; k < P4EST_DIM; k++) {
+        int                 id = idx[k];
+
+        if (id >= 0) {
+          tempx[k] |= (ti & (1 << (base + id))) >> (shift + id);
+        }
+      }
+    }
+    for (k = 0; k < P4EST_DIM; k++) {
+      tx[k] += (tempx[k] << lastlog);
+    }
+    lastlog += logi;
+    ti >>= (P4EST_DIM - i) * logi;
+  }
+  tx[rankx[P4EST_DIM - 1]] += (ti << lastlog);
+}
+
+static inline       p4est_topidx_t
+brick_xyz_to_linear (const p4est_topidx_t tx[P4EST_DIM],
+                     const int logx[P4EST_DIM], const int rankx[P4EST_DIM])
+{
+  int                 i, j, k;
+  int                 lastlog = logx[rankx[P4EST_DIM - 2]];
+  p4est_topidx_t      ti = tx[rankx[P4EST_DIM - 1]] >> lastlog;
+
+  for (i = P4EST_DIM - 2; i >= 0; i--) {
+    p4est_topidx_t      tempx[3] = { 0, 0, 0 };
+    int                 logi =
+      (i == 0) ? lastlog : lastlog - logx[rankx[i - 1]];
+    int                 idx[3] = { -1, -1, -1 };
+    int                 c = 0;
+
+    for (k = 0; k < P4EST_DIM - i; k++) {
+      int                 d = rankx[i + k];
+
+      idx[d] = 0;
+    }
+    for (k = 0; k < P4EST_DIM; k++) {
+      if (idx[k] == 0) {
+        idx[k] = c++;
+      }
+    }
+
+    ti <<= (P4EST_DIM - i) * logi;
+    lastlog -= logi;
+    for (k = 0; k < P4EST_DIM; k++) {
+      tempx[k] = tx[k] >> lastlog;
+    }
+    for (j = 0; j < logi; j++) {
+      int                 shift = (P4EST_DIM - i - 1) * j;
+
+      for (k = 0; k < P4EST_DIM; k++) {
+        int                 id = idx[k];
+
+        if (id >= 0) {
+          ti |= (tempx[k] & (1 << j)) << (shift + id);
+        }
+      }
+    }
+  }
+
+  return ti;
+}
+
+p4est_connectivity_t *
+#ifndef P4_TO_P8
+p4est_connectivity_new_brick (int mi, int ni, int periodic_a, int periodic_b)
+#else
+p8est_connectivity_new_brick (int mi, int ni, int pi, int periodic_a,
+                              int periodic_b, int periodic_c)
+#endif
+{
+#ifndef P4_TO_P8
+  const p4est_topidx_t m = (p4est_topidx_t) mi;
+  const p4est_topidx_t n = (p4est_topidx_t) ni;
+  const p4est_topidx_t mc = periodic_a ? m : (m - 1);
+  const p4est_topidx_t nc = periodic_b ? n : (n - 1);
+  const p4est_topidx_t num_trees = m * n;
+  const p4est_topidx_t num_corners = mc * nc;
+  const p4est_topidx_t num_ctt = P4EST_CHILDREN * num_corners;
+  const p4est_topidx_t num_vertices = (m + 1) * (n + 1);
+  const int           periodic[P4EST_DIM] = { periodic_a, periodic_b };
+  const p4est_topidx_t max[P4EST_DIM] = { m - 1, n - 1 };
+#else
+  const p4est_topidx_t m = (p4est_topidx_t) mi;
+  const p4est_topidx_t n = (p4est_topidx_t) ni;
+  const p4est_topidx_t p = (p4est_topidx_t) pi;
+  const p4est_topidx_t mc = periodic_a ? m : (m - 1);
+  const p4est_topidx_t nc = periodic_b ? n : (n - 1);
+  const p4est_topidx_t pc = periodic_c ? p : (p - 1);
+  const p4est_topidx_t num_trees = m * n * p;
+  const p4est_topidx_t num_corners = mc * nc * pc;
+  const p4est_topidx_t num_ctt = P4EST_CHILDREN * num_corners;
+  const p4est_topidx_t num_edges = m * nc * pc + mc * n * pc + mc * nc * p;
+  const p4est_topidx_t num_ett = 4 * num_edges;
+  const p4est_topidx_t num_vertices = (m + 1) * (n + 1) * (p + 1);
+  const int           periodic[P4EST_DIM] = { periodic_a, periodic_b,
+    periodic_c
+  };
+  const p4est_topidx_t max[P4EST_DIM] = { m - 1, n - 1, p - 1 };
+#endif
+  double             *vertices;
+  p4est_topidx_t     *tree_to_vertex;
+  p4est_topidx_t     *tree_to_tree;
+  int8_t             *tree_to_face;
+  p4est_topidx_t     *tree_to_corner;
+  p4est_topidx_t     *ctt_offset;
+  p4est_topidx_t     *corner_to_tree;
+  int8_t             *corner_to_corner;
+  p4est_topidx_t      n_iter;
+  int                 logx[P4EST_DIM];
+  int                 rankx[P4EST_DIM];
+  int                 i, j, l;
+  p4est_topidx_t      ti, tj, tk;
+  p4est_topidx_t      tx, ty;
+  p4est_topidx_t      tf[P4EST_FACES], tc[P4EST_CHILDREN];
+  p4est_topidx_t      coord[P4EST_DIM], coord2[P4EST_DIM], ttemp;
+  p4est_topidx_t     *linear_to_tree;
+  p4est_topidx_t     *tree_to_corner2;
+  p4est_topidx_t      vcount = 0, vicount = 0;
+  int                 c[P4EST_DIM];
+  p4est_connectivity_t *conn;
+#ifdef P4_TO_P8
+  p4est_topidx_t      tl;
+  p4est_topidx_t      tz;
+  p4est_topidx_t      te[P8EST_EDGES];
+  p4est_topidx_t     *tree_to_edge;
+  p4est_topidx_t     *ett_offset;
+  p4est_topidx_t     *edge_to_tree;
+  int8_t             *edge_to_edge;
+  p4est_topidx_t     *tree_to_edge2;
+  int                 dir1, dir2;
+#endif
+
+#ifndef P4_TO_P8
+  P4EST_ASSERT (m > 0 && n > 0);
+#else
+  P4EST_ASSERT (m > 0 && n > 0 && p > 0);
+#endif
+
+  conn = p4est_connectivity_new (num_vertices, num_trees,
+#ifdef P4_TO_P8
+                                 num_edges, num_ett,
+#endif
+                                 num_corners, num_ctt);
+
+  vertices = conn->vertices;
+  tree_to_vertex = conn->tree_to_vertex;
+  tree_to_tree = conn->tree_to_tree;
+  tree_to_face = conn->tree_to_face;
+#ifdef P4_TO_P8
+  tree_to_edge = conn->tree_to_edge;
+  ett_offset = conn->ett_offset;
+  edge_to_tree = conn->edge_to_tree;
+  edge_to_edge = conn->edge_to_edge;
+#endif
+  tree_to_corner = conn->tree_to_corner;
+  ctt_offset = conn->ctt_offset;
+  corner_to_tree = conn->corner_to_tree;
+  corner_to_corner = conn->corner_to_corner;
+
+#ifdef P4_TO_P8
+  for (ti = 0; ti < num_edges + 1; ti++) {
+    ett_offset[ti] = 4 * ti;
+  }
+#endif
+
+  for (ti = 0; ti < num_corners + 1; ti++) {
+    ctt_offset[ti] = P4EST_CHILDREN * ti;
+  }
+
+  for (ti = 0; ti < P4EST_CHILDREN * num_trees; ti++) {
+    tree_to_vertex[ti] = -1;
+  }
+
+  logx[0] = SC_LOG2_32 (m - 1) + 1;
+  logx[1] = SC_LOG2_32 (n - 1) + 1;
+  n_iter = (1 << logx[0]) * (1 << logx[1]);
+  if (logx[0] <= logx[1]) {
+    rankx[0] = 0;
+    rankx[1] = 1;
+  }
+  else {
+    rankx[0] = 1;
+    rankx[1] = 0;
+  }
+#ifdef P4_TO_P8
+  logx[2] = SC_LOG2_32 (p - 1) + 1;
+  n_iter *= (1 << logx[2]);
+  if (logx[2] < logx[rankx[0]]) {
+    rankx[2] = rankx[1];
+    rankx[1] = rankx[0];
+    rankx[0] = 2;
+  }
+  else if (logx[rankx[1]] <= logx[2]) {
+    rankx[2] = 2;
+  }
+  else {
+    rankx[2] = rankx[1];
+    rankx[1] = 2;
+  }
+#endif
+
+  linear_to_tree = P4EST_ALLOC (p4est_topidx_t, n_iter);
+  tree_to_corner2 = P4EST_ALLOC (p4est_topidx_t, num_trees);
+#ifdef P4_TO_P8
+  tree_to_edge2 = P4EST_ALLOC (p4est_topidx_t, 3 * num_trees);
+#endif
+
+  tj = 0;
+  tk = 0;
+#ifdef P4_TO_P8
+  tl = 0;
+#endif
+  for (ti = 0; ti < n_iter; ti++) {
+    brick_linear_to_xyz (ti, logx, rankx, coord);
+    tx = coord[0];
+    ty = coord[1];
+#ifdef P4_TO_P8
+    tz = coord[2];
+#endif
+    if (tx < m && ty < n &&
+#ifdef P4_TO_P8
+        tz < p &&
+#endif
+        1) {
+      linear_to_tree[ti] = tj;
+      if ((tx < m - 1 || periodic_a) && (ty < n - 1 || periodic_b) &&
+#ifdef P4_TO_P8
+          (tz < p - 1 || periodic_c) &&
+#endif
+          1) {
+        tree_to_corner2[tj] = tk++;
+#ifdef P4_TO_P8
+        tree_to_edge2[3 * tj] = tl++;
+        tree_to_edge2[3 * tj + 1] = tl++;
+        tree_to_edge2[3 * tj + 2] = tl++;
+#endif
+      }
+      else {
+        tree_to_corner2[tj] = -1;
+#ifdef P4_TO_P8
+        if ((ty < n - 1 || periodic_b) && (tz < p - 1 || periodic_c)) {
+          tree_to_edge2[3 * tj] = tl++;
+        }
+        else {
+          tree_to_edge2[3 * tj] = -1;
+        }
+        if ((tx < m - 1 || periodic_a) && (tz < p - 1 || periodic_c)) {
+          tree_to_edge2[3 * tj + 1] = tl++;
+        }
+        else {
+          tree_to_edge2[3 * tj + 1] = -1;
+        }
+        if ((tx < m - 1 || periodic_a) && (ty < n - 1 || periodic_b)) {
+          tree_to_edge2[3 * tj + 2] = tl++;
+        }
+        else {
+          tree_to_edge2[3 * tj + 2] = -1;
+        }
+#endif
+      }
+      tj++;
+    }
+    else {
+      linear_to_tree[ti] = -1;
+    }
+  }
+  P4EST_ASSERT (tj == num_trees);
+  P4EST_ASSERT (tk == num_corners);
+#ifdef P4_TO_P8
+  P4EST_ASSERT (tl == num_edges);
+#endif
+
+  for (ti = 0; ti < n_iter; ti++) {
+    brick_linear_to_xyz (ti, logx, rankx, coord);
+    tx = coord[0];
+    ty = coord[1];
+#ifdef P4_TO_P8
+    tz = coord[2];
+#endif
+    if (tx < m && ty < n &&
+#ifdef P4_TO_P8
+        tz < p &&
+#endif
+        1) {
+      tj = linear_to_tree[ti];
+      P4EST_ASSERT (tj >= 0);
+      for (i = 0; i < P4EST_DIM; i++) {
+        for (j = 0; j < 2; j++) {
+          l = 2 * i + j;
+          coord2[0] = ((tx + ((i == 0) ? (2 * j - 1) : 0)) + m) % m;
+          coord2[1] = ((ty + ((i == 1) ? (2 * j - 1) : 0)) + n) % n;
+#ifdef P4_TO_P8
+          coord2[2] = ((tz + ((i == 2) ? (2 * j - 1) : 0)) + p) % p;
+#endif
+          tf[l] = brick_xyz_to_linear (coord2, logx, rankx);
+          P4EST_ASSERT (tf[l] < n_iter);
+          tf[l] = linear_to_tree[tf[l]];
+          P4EST_ASSERT (tf[l] >= 0);
+        }
+#ifdef P4_TO_P8
+        for (j = 0; j < 4; j++) {
+          l = 4 * i + j;
+          coord2[0] = ((tx + ((i == 0) ? 0 : (2 * (j & 1) - 1))) + m) % m;
+          coord2[1] = ((ty + ((i == 1) ? 0 :
+                              (2 * ((i == 0) ? (j & 1) : (j / 2)) - 1))) +
+                       n) % n;
+          coord2[2] = ((tz + ((i == 2) ? 0 : (2 * (j / 2) - 1))) + p) % p;
+          te[l] = brick_xyz_to_linear (coord2, logx, rankx);
+          P4EST_ASSERT (te[l] < n_iter);
+          te[l] = linear_to_tree[te[l]];
+          P4EST_ASSERT (te[l] >= 0);
+        }
+#endif
+      }
+      for (i = 0; i < P4EST_CHILDREN; i++) {
+        coord2[0] = ((tx + (((i & 1) == 0) ? -1 : 1)) + m) % m;
+        coord2[1] = ((ty + ((((i >> 1) & 1) == 0) ? -1 : 1)) + n) % n;
+#ifdef P4_TO_P8
+        coord2[2] = ((tz + (((i >> 2) == 0) ? -1 : 1)) + p) % p;
+#endif
+        tc[i] = brick_xyz_to_linear (coord2, logx, rankx);
+        P4EST_ASSERT (tc[i] < n_iter);
+        tc[i] = linear_to_tree[tc[i]];
+        P4EST_ASSERT (tc[i] >= 0);
+      }
+      for (i = 0; i < P4EST_DIM; i++) {
+        for (j = 0; j < 2; j++) {
+          l = i * 2 + j;
+          if (!periodic[i] &&
+              ((coord[i] == 0 && j == 0) || (coord[i] == max[i] && j == 1))) {
+            tree_to_tree[tj * P4EST_FACES + l] = tj;
+            tree_to_face[tj * P4EST_FACES + l] = (int8_t) l;
+          }
+          else {
+            tree_to_tree[tj * P4EST_FACES + l] = tf[l];
+            tree_to_face[tj * P4EST_FACES + l] = (int8_t) (i * 2 + (j ^ 1));
+          }
+        }
+#ifdef P4_TO_P8
+        if (tree_to_edge != NULL) {
+          /** dir1, dir2 should be in correct z order */
+          dir1 = (i == 0) ? 1 : 0;
+          dir2 = (i == 2) ? 1 : 2;
+          for (j = 0; j < 4; j++) {
+            l = i * 4 + j;
+            if ((!periodic[dir1] &&
+                 ((coord[dir1] == 0 && (j & 1) == 0) ||
+                  (coord[dir1] == max[dir1] && (j & 1) == 1))) ||
+                (!periodic[dir2] &&
+                 ((coord[dir2] == 0 && (j / 2) == 0) ||
+                  (coord[dir2] == max[dir2] && (j / 2) == 1)))) {
+              tree_to_edge[tj * P8EST_EDGES + l] = -1;
+            }
+            else {
+              switch (j) {
+              case 0:
+                ttemp = tree_to_edge2[te[l] * 3 + i];
+                break;
+              case 1:
+                ttemp = tree_to_edge2[tf[dir2 * 2] * 3 + i];
+                break;
+              case 2:
+                ttemp = tree_to_edge2[tf[dir1 * 2] * 3 + i];
+                break;
+              case 3:
+                ttemp = tree_to_edge2[tj * 3 + i];
+                break;
+              default:
+                SC_ABORT_NOT_REACHED ();
+              }
+              P4EST_ASSERT (ttemp >= 0);
+              tree_to_edge[tj * P8EST_EDGES + l] = ttemp;
+              edge_to_tree[4 * ttemp + (3 - j)] = tj;
+              edge_to_edge[4 * ttemp + (3 - j)] = (int8_t) l;
+            }
+          }
+        }
+#endif
+      }
+      for (i = 0; i < P4EST_CHILDREN; i++) {
+        if (tree_to_corner != NULL) {
+          c[0] = i & 1;
+          c[1] = (i >> 1) & 1;
+#ifdef P4_TO_P8
+          c[2] = i >> 2;
+#endif
+          if ((!periodic[0] &&
+               ((coord[0] == 0 && c[0] == 0) ||
+                (coord[0] == max[0] && c[0] == 1))) ||
+              (!periodic[1] &&
+               ((coord[1] == 0 && c[1] == 0) ||
+                (coord[1] == max[1] && c[1] == 1))) ||
+#ifdef P4_TO_P8
+              (!periodic[2] &&
+               ((coord[2] == 0 && c[2] == 0) ||
+                (coord[2] == max[2] && c[2] == 1))) ||
+#endif
+              0) {
+            tree_to_corner[tj * P4EST_CHILDREN + i] = -1;
+          }
+          else {
+            switch (i) {
+#ifndef P4_TO_P8
+            case 0:
+              ttemp = tc[0];
+              break;
+            case 1:
+              ttemp = tf[2];
+              break;
+            case 2:
+              ttemp = tf[0];
+              break;
+            case 3:
+              ttemp = tj;
+              break;
+#else
+            case 0:
+              ttemp = tc[0];
+              break;
+            case 1:
+              ttemp = te[0];
+              break;
+            case 2:
+              ttemp = te[4];
+              break;
+            case 3:
+              ttemp = tf[4];
+              break;
+            case 4:
+              ttemp = te[8];
+              break;
+            case 5:
+              ttemp = tf[2];
+              break;
+            case 6:
+              ttemp = tf[0];
+              break;
+            case 7:
+              ttemp = tj;
+              break;
+#endif
+            default:
+              SC_ABORT_NOT_REACHED ();
+            }
+            ttemp = tree_to_corner2[ttemp];
+            P4EST_ASSERT (ttemp >= 0);
+            tree_to_corner[tj * P4EST_CHILDREN + i] = ttemp;
+            corner_to_tree[ttemp * P4EST_CHILDREN +
+                           (P4EST_CHILDREN - 1 - i)] = tj;
+            corner_to_corner[ttemp * P4EST_CHILDREN +
+                             (P4EST_CHILDREN - 1 - i)] = (int8_t) i;
+          }
+        }
+#ifdef P4_TO_P8
+        if (tz > 0 && (i >> 2) == 0) {
+          tree_to_vertex[tj * P4EST_CHILDREN + i] =
+            tree_to_vertex[tf[4] * P4EST_CHILDREN + i + 4];
+        }
+        else
+#endif
+        if (ty > 0 && ((i >> 1) & 1) == 0) {
+          tree_to_vertex[tj * P4EST_CHILDREN + i] =
+            tree_to_vertex[tf[2] * P4EST_CHILDREN + i + 2];
+        }
+        else if (tx > 0 && (i & 1) == 0) {
+          tree_to_vertex[tj * P4EST_CHILDREN + i] =
+            tree_to_vertex[tf[0] * P4EST_CHILDREN + i + 1];
+        }
+        else {
+          tree_to_vertex[tj * P4EST_CHILDREN + i] = vcount++;
+          vertices[vicount++] = (double) (tx + (i & 1));
+          vertices[vicount++] = (double) (ty + ((i >> 1) & 1));
+#ifndef P4_TO_P8
+          vertices[vicount++] = 0.;
+#else
+          vertices[vicount++] = (double) (tz + (i >> 2));
+#endif
+        }
+      }
+    }
+  }
+
+  P4EST_ASSERT (vcount == num_vertices);
+
+  P4EST_FREE (linear_to_tree);
+  P4EST_FREE (tree_to_corner2);
+#ifdef P4_TO_P8
+  P4EST_FREE (tree_to_edge2);
+#endif
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+
+  return conn;
+}
+
+p4est_connectivity_t *
+p4est_connectivity_new_byname (const char *name)
+{
+#ifndef P4_TO_P8
+  if (!strcmp (name, "brick23")) {
+    return p4est_connectivity_new_brick (2, 3, 0, 0);
+  }
+  else if (!strcmp (name, "corner")) {
+    return p4est_connectivity_new_corner ();
+  }
+  else if (!strcmp (name, "cubed")) {
+    return p4est_connectivity_new_cubed ();
+  }
+  else if (!strcmp (name, "disk")) {
+    return p4est_connectivity_new_disk ();
+  }
+  else if (!strcmp (name, "moebius")) {
+    return p4est_connectivity_new_moebius ();
+  }
+  else if (!strcmp (name, "periodic")) {
+    return p4est_connectivity_new_periodic ();
+  }
+  else if (!strcmp (name, "pillow")) {
+    return p4est_connectivity_new_pillow ();
+  }
+  else if (!strcmp (name, "rotwrap")) {
+    return p4est_connectivity_new_rotwrap ();
+  }
+  else if (!strcmp (name, "star")) {
+    return p4est_connectivity_new_star ();
+  }
+  else if (!strcmp (name, "unit")) {
+    return p4est_connectivity_new_unitsquare ();
+  }
+#else
+  if (!strcmp (name, "brick235")) {
+    return p8est_connectivity_new_brick (2, 3, 5, 0, 0, 0);
+  }
+  else if (!strcmp (name, "periodic")) {
+    return p8est_connectivity_new_periodic ();
+  }
+  else if (!strcmp (name, "rotcubes")) {
+    return p8est_connectivity_new_rotcubes ();
+  }
+  else if (!strcmp (name, "rotwrap")) {
+    return p8est_connectivity_new_rotwrap ();
+  }
+  else if (!strcmp (name, "shell")) {
+    return p8est_connectivity_new_shell ();
+  }
+  else if (!strcmp (name, "sphere")) {
+    return p8est_connectivity_new_sphere ();
+  }
+  else if (!strcmp (name, "twocubes")) {
+    return p8est_connectivity_new_twocubes ();
+  }
+  else if (!strcmp (name, "twowrap")) {
+    return p8est_connectivity_new_twowrap ();
+  }
+  else if (!strcmp (name, "unit")) {
+    return p8est_connectivity_new_unitcube ();
+  }
+#endif
+  return NULL;
+}
+
+typedef struct
+{
+  p4est_topidx_t      key[P4EST_HALF];
+  p4est_topidx_t      trees[2];
+  int8_t              faces[2];
+}
+p4est_conn_face_info_t;
+
+static unsigned
+p4est_conn_face_hash (const void *v, const void *u)
+{
+  const p4est_conn_face_info_t *fi = (p4est_conn_face_info_t *) v;
+
+#ifdef P4_TO_P8
+  return p4est_topidx_hash4 (fi->key);
+#else
+  return p4est_topidx_hash2 (fi->key);
+#endif
+}
+
+static int
+p4est_conn_face_equal (const void *v1, const void *v2, const void *u)
+{
+  const p4est_conn_face_info_t *fi1 = (p4est_conn_face_info_t *) v1;
+  const p4est_conn_face_info_t *fi2 = (p4est_conn_face_info_t *) v2;
+
+  return !memcmp (fi1->key, fi2->key, P4EST_HALF * sizeof (p4est_topidx_t));
+}
+
+static void
+p4est_conn_face_key (p4est_topidx_t * key, p4est_topidx_t * ttv, int face)
+{
+  int                 fc;
+
+  P4EST_ASSERT (0 <= face && face < P4EST_FACES);
+
+  for (fc = 0; fc < P4EST_HALF; ++fc) {
+    key[fc] = ttv[p4est_face_corners[face][fc]];
+  }
+  p4est_topidx_bsort (key, P4EST_HALF);
+}
+
+#ifdef P4_TO_P8
+
+typedef struct
+{
+  p4est_topidx_t      key[2];
+  sc_array_t          trees;
+  sc_array_t          edges;
+  p4est_topidx_t      edgeid;
+}
+p8est_conn_edge_info_t;
+
+static unsigned
+p8est_conn_edge_hash (const void *v, const void *u)
+{
+  const p8est_conn_edge_info_t *ei = (p8est_conn_edge_info_t *) v;
+
+  return p4est_topidx_hash2 (ei->key);
+}
+
+static int
+p8est_conn_edge_equal (const void *v1, const void *v2, const void *u)
+{
+  const p8est_conn_edge_info_t *ei1 = (p8est_conn_edge_info_t *) v1;
+  const p8est_conn_edge_info_t *ei2 = (p8est_conn_edge_info_t *) v2;
+
+  return !memcmp (ei1->key, ei2->key, 2 * sizeof (p4est_topidx_t));
+}
+
+static void
+p8est_conn_edge_key (p4est_topidx_t * key, p4est_topidx_t * ttv, int edge)
+{
+  int                 ec;
+
+  P4EST_ASSERT (0 <= edge && edge < P8EST_EDGES);
+
+  for (ec = 0; ec < 2; ++ec) {
+    key[ec] = ttv[p8est_edge_corners[edge][ec]];
+  }
+  p4est_topidx_bsort (key, 2);
+}
+
+#endif /* P4_TO_P8 */
+
+static void
+p4est_expand_face_transform_internal (int iface, int target_face,
+                                      int orientation, int ftransform[])
+{
+#ifdef P4_TO_P8
+  int                 reverse;
+#ifdef P4EST_ENABLE_DEBUG
+  int                 i;
+  int                *my_axis = &ftransform[0];
+  int                *target_axis = &ftransform[3];
+#endif
+#endif
+
+  P4EST_ASSERT (0 <= iface && iface < P4EST_FACES);
+  P4EST_ASSERT (0 <= target_face && target_face < P4EST_FACES);
+  P4EST_ASSERT (0 <= orientation && orientation < P4EST_HALF);
+
+#ifdef P4_TO_P8
+  /* the code that was here before is now in test/test_face_transform3.c */
+  ftransform[0] = iface < 2 ? 1 : 0;
+  ftransform[1] = iface < 4 ? 2 : 1;
+  ftransform[2] = iface / 2;
+  reverse =
+    p8est_face_permutation_refs[0][iface] ^
+    p8est_face_permutation_refs[0][target_face] ^
+    (orientation == 0 || orientation == 3);
+  ftransform[3 + reverse] = target_face < 2 ? 1 : 0;
+  ftransform[3 + !reverse] = target_face < 4 ? 2 : 1;
+  ftransform[5] = target_face / 2;
+  reverse = (p8est_face_permutation_refs[iface][target_face] == 1);
+  ftransform[6 + reverse] = (orientation & 1);
+  ftransform[6 + !reverse] = (orientation >> 1);
+  ftransform[8] = 2 * (iface & 1) + (target_face & 1);
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (i = 0; i < 3; ++i) {
+    P4EST_ASSERT (0 <= my_axis[i] && my_axis[i] < 3);
+    P4EST_ASSERT (0 <= target_axis[i] && target_axis[i] < 3);
+  }
+  P4EST_ASSERT (my_axis[0] != my_axis[1] &&
+                my_axis[0] != my_axis[2] && my_axis[1] != my_axis[2]);
+  P4EST_ASSERT (target_axis[0] != target_axis[1] &&
+                target_axis[0] != target_axis[2] &&
+                target_axis[1] != target_axis[2]);
+#endif
+#else
+  ftransform[2] = iface / 2;
+  ftransform[1] = 0;
+  ftransform[0] = 1 - ftransform[2];
+  ftransform[5] = target_face / 2;
+  ftransform[4] = 0;
+  ftransform[3] = 1 - ftransform[5];
+  ftransform[6] = orientation;
+  ftransform[7] = 0;
+  ftransform[8] = 2 * (iface & 1) + (target_face & 1);
+#endif
+}
+
+void
+p4est_expand_face_transform (int iface, int nface, int ftransform[])
+{
+  const int           target_face = nface % P4EST_FACES;
+  const int           orientation = nface / P4EST_FACES;
+
+  p4est_expand_face_transform_internal (iface, target_face, orientation,
+                                        ftransform);
+}
+
+p4est_topidx_t
+p4est_find_face_transform (p4est_connectivity_t * connectivity,
+                           p4est_topidx_t itree, int iface, int ftransform[])
+{
+  int                 target_code, target_face, orientation;
+  p4est_topidx_t      target_tree;
+
+  P4EST_ASSERT (itree >= 0 && itree < connectivity->num_trees);
+  P4EST_ASSERT (iface >= 0 && iface < P4EST_FACES);
+
+  target_tree = connectivity->tree_to_tree[P4EST_FACES * itree + iface];
+  target_code = (int) connectivity->tree_to_face[P4EST_FACES * itree + iface];
+  target_face = target_code % P4EST_FACES;
+  orientation = target_code / P4EST_FACES;
+
+  if (target_tree == itree && target_face == iface) {
+    P4EST_ASSERT (orientation == 0);
+    return -1;
+  }
+
+  p4est_expand_face_transform_internal (iface, target_face, orientation,
+                                        ftransform);
+
+  return target_tree;
+}
+
+static int
+p4est_find_corner_transform_internal (p4est_connectivity_t * conn,
+                                      p4est_topidx_t itree, int icorner,
+                                      p4est_corner_info_t * ci,
+                                      p4est_topidx_t * ctt, int8_t * ctc,
+                                      p4est_topidx_t corner_trees,
+                                      p4est_topidx_t ntree[P4EST_DIM])
+{
+  int                 i;
+  int                 iface[P4EST_DIM], nface[P4EST_DIM];
+  int                 orient[P4EST_DIM], fcorner[P4EST_DIM];
+  int                 ncorner, ncode;
+  int                 fc, nc;
+  int                 omit;
+  p4est_topidx_t      ctree, nctree;
+#ifdef P4_TO_P8
+  int                 iedge[3], iwhich[3];
+  int                 pref, pset;
+  size_t              jz;
+  p4est_topidx_t      aedge[3];
+  p8est_edge_info_t   ei[3];
+  sc_array_t         *eta[3];
+  p8est_edge_transform_t *et;
+#endif
+  sc_array_t         *cta = &ci->corner_transforms;
+  p4est_corner_transform_t *ct;
+  int                 edge_ignored = 0;
+
+  P4EST_ASSERT (0 <= itree && itree < conn->num_trees);
+  P4EST_ASSERT (0 <= icorner && icorner < P4EST_CHILDREN);
+  P4EST_ASSERT (cta->elem_size == sizeof (p4est_corner_transform_t));
+
+  /* find the face neighbors */
+  for (i = 0; i < P4EST_DIM; ++i) {
+    iface[i] = p4est_corner_faces[icorner][i];
+    ntree[i] = conn->tree_to_tree[P4EST_FACES * itree + iface[i]];
+    ncode = (int) conn->tree_to_face[P4EST_FACES * itree + iface[i]];
+    if (ntree[i] == itree && ncode == iface[i]) {       /* domain boundary */
+      ntree[i] = -1;
+      nface[i] = -1;
+      orient[i] = -1;
+      fcorner[i] = -1;
+    }
+    else {
+      nface[i] = ncode % P4EST_FACES;
+      orient[i] = ncode / P4EST_FACES;
+      fcorner[i] = p4est_corner_face_corners[icorner][iface[i]];
+      P4EST_ASSERT (fcorner[i] >= 0);
+    }
+  }
+
+#ifdef P4_TO_P8
+  /* find the three edge transforms */
+  if (conn->num_edges == 0) {
+    eta[0] = eta[1] = eta[2] = NULL;
+    aedge[0] = aedge[1] = aedge[2] = -1;
+  }
+  else {
+    for (i = 0; i < 3; ++i) {
+      iedge[i] = p8est_corner_edges[icorner][i];
+      aedge[i] = conn->tree_to_edge[P8EST_EDGES * itree + iedge[i]];
+      if (aedge[i] == -1) {
+        eta[i] = NULL;
+        continue;
+      }
+      iwhich[i] = (p8est_edge_corners[iedge[i]][1] == icorner);
+      P4EST_ASSERT (p8est_edge_corners[iedge[i]][iwhich[i]] == icorner);
+
+      eta[i] = &ei[i].edge_transforms;
+      sc_array_init (eta[i], sizeof (p8est_edge_transform_t));
+      p8est_find_edge_transform (conn, itree, iedge[i], &ei[i]);
+    }
+  }
+#endif
+
+  /* collect all corners that are not from face or edge neighbors */
+  for (ctree = 0; ctree < corner_trees; ++ctree) {
+    nctree = ctt[ctree];
+    P4EST_ASSERT (0 <= nctree && nctree < conn->num_trees);
+    ncorner = (int) ctc[ctree];
+    P4EST_ASSERT (ncorner >= 0 && ncorner < P4EST_CHILDREN);
+    if (ncorner == icorner && nctree == itree) {
+      continue;
+    }
+
+    /* rule out face neighbors */
+    omit = 0;
+    for (i = 0; i < P4EST_DIM; ++i) {
+      if (nctree == ntree[i]) {
+        P4EST_ASSERT (fcorner[i] >= 0);
+#ifdef P4_TO_P8
+        pref = p8est_face_permutation_refs[iface[i]][nface[i]];
+        pset = p8est_face_permutation_sets[pref][orient[i]];
+        fc = p8est_face_permutations[pset][fcorner[i]];
+#else
+        fc = fcorner[i] ^ orient[i];
+#endif
+        nc = p4est_face_corners[nface[i]][fc];
+
+        if (nc == ncorner) {
+          omit = 1;
+          break;
+        }
+      }
+    }
+    if (omit)
+      continue;
+
+#ifdef P4_TO_P8
+    /* rule out edge neighbors */
+    omit = 0;
+    for (i = 0; i < 3; ++i) {
+      if (aedge[i] == -1) {
+        continue;
+      }
+      for (jz = 0; jz < eta[i]->elem_count; ++jz) {
+        et = p8est_edge_array_index (eta[i], jz);
+        if (nctree == et->ntree) {
+          P4EST_ASSERT ((iwhich[i] & ~1) == 0);
+          nc = p8est_edge_corners[et->nedge][et->nflip ^ iwhich[i]];
+
+          if (nc == ncorner) {
+            omit = 1;
+            break;
+          }
+        }
+      }
+      if (omit)
+        break;
+    }
+    if (omit) {
+      ++edge_ignored;
+      continue;
+    }
+#endif
+
+    /* else we have a true all-diagonal corner with ntree */
+    ct = (p4est_corner_transform_t *) sc_array_push (cta);
+    ct->ntree = nctree;
+    ct->ncorner = (int8_t) ncorner;
+  }
+
+#ifdef P4_TO_P8
+  for (i = 0; i < 3; ++i) {
+    if (aedge[i] >= 0) {
+      sc_array_reset (eta[i]);
+    }
+  }
+#endif
+
+  return edge_ignored;
+}
+
+void
+p4est_find_corner_transform (p4est_connectivity_t * conn,
+                             p4est_topidx_t itree, int icorner,
+                             p4est_corner_info_t * ci)
+{
+  int                 ignored;
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              expected_count;
+#endif
+  p4est_topidx_t      ntree[P4EST_DIM], corner_trees, acorner, cttac;
+  sc_array_t         *cta = &ci->corner_transforms;
+
+  P4EST_ASSERT (0 <= itree && itree < conn->num_trees);
+  P4EST_ASSERT (0 <= icorner && icorner < P4EST_CHILDREN);
+  P4EST_ASSERT (cta->elem_size == sizeof (p4est_corner_transform_t));
+
+  /* check if this corner exists at all */
+  ci->icorner = (int8_t) icorner;
+  sc_array_resize (cta, 0);
+  if (conn->num_corners == 0) {
+    return;
+  }
+  acorner = conn->tree_to_corner[P4EST_CHILDREN * itree + icorner];
+  if (acorner == -1) {
+    return;
+  }
+  P4EST_ASSERT (0 <= acorner && acorner < conn->num_corners);
+
+  /* retrieve connectivity information for this corner */
+  cttac = conn->ctt_offset[acorner];
+  corner_trees = conn->ctt_offset[acorner + 1] - cttac;
+  P4EST_ASSERT (0 <= cttac && 1 <= corner_trees);
+
+  /* loop through all corner neighbors and find corner connections */
+  ignored = p4est_find_corner_transform_internal (conn, itree, icorner, ci,
+                                                  conn->corner_to_tree +
+                                                  cttac,
+                                                  conn->corner_to_corner +
+                                                  cttac, corner_trees, ntree);
+#ifdef P4EST_ENABLE_DEBUG
+  expected_count = cta->elem_count + 1 + (ntree[0] != -1) + (ntree[1] != -1);
+#ifdef P4_TO_P8
+  expected_count += (ntree[2] != -1);
+#else
+  P4EST_ASSERT (ignored == 0);
+#endif
+
+  P4EST_ASSERT (corner_trees == (p4est_topidx_t) (expected_count + ignored));
+#endif
+}
+
+void
+p4est_connectivity_complete (p4est_connectivity_t * conn)
+{
+  int                 face, corner, r;
+  int                 primary, secondary, j;
+  size_t              pz;
+  p4est_topidx_t     *pt, treeid, nodeid, tt;
+  p4est_topidx_t     *ttv, *whichttv[2];
+  p4est_conn_face_info_t fikey, *fi;
+  sc_hash_array_t    *face_ha;
+#ifdef P4_TO_P8
+  int                 edge;
+  int8_t             *et;
+  size_t              ez, egz;
+  p4est_topidx_t     *ept, real_edges, enode[2];
+  p4est_topidx_t      ett_count, ett_offset, ett_edge;
+  p8est_conn_edge_info_t eikey, *ei;
+  p8est_edge_info_t   einfo;
+  sc_hash_array_t    *edge_ha;
+  sc_array_t          edge_array, edge_to_pz;
+  sc_array_t         *eta = &einfo.edge_transforms;
+#endif
+  int                 ignored;
+  int8_t             *ct;
+  size_t              zcount;
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              expected_count;
+#endif
+  p4est_topidx_t      real_corners, ntree[P4EST_DIM];
+  p4est_topidx_t      ctt_count, ctt_offset, ctt_corner;
+  p4est_corner_info_t cinfo;
+  sc_array_t         *node_trees, *nt;
+  sc_array_t         *node_corners, *nc;
+  sc_array_t         *cta = &cinfo.corner_transforms;
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+
+  /* prepare data structures and remove previous connectivity information */
+  face_ha = sc_hash_array_new (sizeof (p4est_conn_face_info_t),
+                               p4est_conn_face_hash, p4est_conn_face_equal,
+                               NULL);
+#ifdef P4_TO_P8
+  edge_ha = sc_hash_array_new (sizeof (p8est_conn_edge_info_t),
+                               p8est_conn_edge_hash, p8est_conn_edge_equal,
+                               NULL);
+  P4EST_FREE (conn->tree_to_edge);
+  P4EST_FREE (conn->ett_offset);
+  P4EST_FREE (conn->edge_to_tree);
+  P4EST_FREE (conn->edge_to_edge);
+  real_edges = P8EST_EDGES * conn->num_trees;
+  conn->tree_to_edge = P4EST_ALLOC (p4est_topidx_t, real_edges);
+  memset (conn->tree_to_edge, -1, real_edges * sizeof (p4est_topidx_t));
+  real_edges = 0;
+  ett_count = 0;
+  sc_array_init (&edge_to_pz, sizeof (p4est_topidx_t));
+  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+#endif
+  P4EST_FREE (conn->tree_to_corner);
+  P4EST_FREE (conn->ctt_offset);
+  P4EST_FREE (conn->corner_to_tree);
+  P4EST_FREE (conn->corner_to_corner);
+  real_corners = P4EST_CHILDREN * conn->num_trees;
+  conn->tree_to_corner = P4EST_ALLOC (p4est_topidx_t, real_corners);
+  memset (conn->tree_to_corner, -1, real_corners * sizeof (p4est_topidx_t));
+  real_corners = 0;
+  ctt_count = 0;
+  node_trees = P4EST_ALLOC (sc_array_t, conn->num_vertices);
+  node_corners = P4EST_ALLOC (sc_array_t, conn->num_vertices);
+  for (nodeid = 0; nodeid < conn->num_vertices; ++nodeid) {
+    sc_array_init (node_trees + nodeid, sizeof (p4est_topidx_t));
+    sc_array_init (node_corners + nodeid, sizeof (int8_t));
+  }
+  sc_array_init (cta, sizeof (p4est_corner_transform_t));
+
+  /* hash all faces and edges to identify connections, map corners */
+  ttv = conn->tree_to_vertex;
+  for (treeid = 0; treeid < conn->num_trees; ++treeid) {
+    for (face = 0; face < P4EST_FACES; ++face) {
+      p4est_conn_face_key (fikey.key, ttv, face);
+      fi = (p4est_conn_face_info_t *)
+        sc_hash_array_insert_unique (face_ha, &fikey, &pz);
+      if (fi != NULL) {
+        /* added fi to hash array as the first of two faces */
+        P4EST_ASSERT (sc_array_position (&face_ha->a, fi) == pz);
+        memcpy (fi->key, fikey.key, P4EST_HALF * sizeof (p4est_topidx_t));
+        fi->trees[0] = treeid;
+        fi->faces[0] = (int8_t) face;
+        fi->trees[1] = -1;
+        fi->faces[1] = -1;
+      }
+      else {
+        /* found existing entry from the first face */
+        fi = (p4est_conn_face_info_t *) sc_array_index (&face_ha->a, pz);
+        P4EST_ASSERT (p4est_conn_face_equal (fi->key, fikey.key, NULL));
+        P4EST_ASSERT (fi->trees[0] >= 0 && fi->faces[0] >= 0);
+        P4EST_ASSERT (fi->trees[1] == -1 && fi->faces[1] == -1);
+        fi->trees[1] = treeid;
+        fi->faces[1] = (int8_t) face;
+
+        /* find primary face and orientation to store it */
+        primary = (fi->faces[0] <= fi->faces[1] ? 0 : 1);
+        secondary = 1 - primary;
+        whichttv[0] = conn->tree_to_vertex + P4EST_CHILDREN * fi->trees[0];
+        whichttv[1] = ttv;
+        nodeid = whichttv[primary][p4est_face_corners[fi->faces[primary]][0]];
+        for (r = 0; r < P4EST_HALF; ++r) {
+          corner = p4est_face_corners[fi->faces[secondary]][r];
+          if (nodeid == whichttv[secondary][corner]) {
+            break;
+          }
+        }
+        P4EST_ASSERT (r < P4EST_HALF);
+        for (j = 0; j < 2; ++j) {
+          tt = P4EST_FACES * fi->trees[j] + fi->faces[j];
+          conn->tree_to_tree[tt] = fi->trees[1 - j];
+          conn->tree_to_face[tt] =
+            (int8_t) (P4EST_FACES * r + fi->faces[1 - j]);
+        }
+      }
+    }
+#ifdef P4_TO_P8
+    for (edge = 0; edge < P8EST_EDGES; ++edge) {
+      p8est_conn_edge_key (eikey.key, ttv, edge);
+      ei = (p8est_conn_edge_info_t *)
+        sc_hash_array_insert_unique (edge_ha, &eikey, &pz);
+      P4EST_ASSERT (conn->tree_to_edge[P8EST_EDGES * treeid + edge] == -1);
+      if (ei != NULL) {
+        /* added ei to hash array as the first of an edge group */
+        P4EST_ASSERT (sc_array_position (&edge_ha->a, ei) == pz);
+        memcpy (ei->key, eikey.key, 2 * sizeof (p4est_topidx_t));
+        ei->edgeid = -1;
+        sc_array_init (&ei->trees, sizeof (p4est_topidx_t));
+        sc_array_init (&ei->edges, sizeof (int8_t));
+      }
+      else {
+        /* this edge has been found before */
+        ei = (p8est_conn_edge_info_t *) sc_array_index (&edge_ha->a, pz);
+        P4EST_ASSERT (p8est_conn_edge_equal (ei->key, eikey.key, NULL));
+        P4EST_ASSERT (ei->trees.elem_count == ei->edges.elem_count);
+        if (ei->trees.elem_count == 1) {
+          /* store number of this real edge and fill previous tree */
+          P4EST_ASSERT (ei->edgeid == -1);
+          ei->edgeid = real_edges++;
+          ept = (p4est_topidx_t *) sc_array_push (&edge_to_pz);
+          *ept = (p4est_topidx_t) pz;
+          pt = (p4est_topidx_t *) sc_array_index (&ei->trees, 0);
+          P4EST_ASSERT (0 <= *pt && *pt < conn->num_trees);
+          et = (int8_t *) sc_array_index (&ei->edges, 0);
+          P4EST_ASSERT (0 <= *et && *et < P8EST_EDGES);
+          P4EST_ASSERT (conn->tree_to_edge[P8EST_EDGES * *pt + *et] == -1);
+          conn->tree_to_edge[P8EST_EDGES * *pt + *et] = ei->edgeid;
+          ett_count += 2;
+        }
+        else {
+          /* check that edge is initialized */
+          P4EST_ASSERT (ei->trees.elem_count > 1);
+          P4EST_ASSERT (0 <= ei->edgeid && ei->edgeid < real_edges);
+          ++ett_count;
+        }
+        conn->tree_to_edge[P8EST_EDGES * treeid + edge] = ei->edgeid;
+      }
+
+      /* store edge information */
+      pt = (p4est_topidx_t *) sc_array_push (&ei->trees);
+      *pt = treeid;
+      et = (int8_t *) sc_array_push (&ei->edges);
+      *et = (int8_t) edge;
+    }
+#endif
+    for (corner = 0; corner < P4EST_CHILDREN; ++corner) {
+      nodeid = ttv[corner];
+      P4EST_ASSERT (0 <= nodeid && nodeid < conn->num_vertices);
+      nt = node_trees + nodeid;
+      nc = node_corners + nodeid;
+      zcount = nt->elem_count;
+      P4EST_ASSERT (zcount == nc->elem_count);
+      if (zcount == 1) {
+        ctt_count += 2;
+      }
+      else if (zcount > 1) {
+        ++ctt_count;
+      }
+      P4EST_ASSERT (conn->tree_to_corner[P4EST_CHILDREN * treeid + corner] ==
+                    -1);
+      conn->tree_to_corner[P4EST_CHILDREN * treeid + corner] = nodeid;
+      pt = (p4est_topidx_t *) sc_array_push (nt);
+      *pt = treeid;
+      ct = (int8_t *) sc_array_push (nc);
+      *ct = (int8_t) corner;
+    }
+    ttv += P4EST_CHILDREN;
+  }
+
+  /* single faces are on the boundary and need not be changed */
+  sc_hash_array_destroy (face_ha);
+
+  /* complete edge identification */
+#ifdef P4_TO_P8
+  P4EST_ASSERT (edge_to_pz.elem_count == (size_t) real_edges);
+  conn->num_edges = real_edges;
+  conn->ett_offset = P4EST_ALLOC (p4est_topidx_t, conn->num_edges + 1);
+  conn->edge_to_tree = P4EST_ALLOC (p4est_topidx_t, ett_count);
+  conn->edge_to_edge = P4EST_ALLOC (int8_t, ett_count);
+  sc_hash_array_rip (edge_ha, &edge_array);
+  ett_edge = 0;
+  ett_offset = 0;
+  real_edges = 0;
+
+  /* loop through all connected edges */
+  for (ez = 0; ez < edge_to_pz.elem_count; ++ez) {
+    ept = (p4est_topidx_t *) sc_array_index (&edge_to_pz, ez);
+    ei = (p8est_conn_edge_info_t *) sc_array_index (&edge_array, *ept);
+    P4EST_ASSERT (ei->trees.elem_count > 1);
+    P4EST_ASSERT (ei->trees.elem_count == ei->edges.elem_count);
+    P4EST_ASSERT (0 <= ei->edgeid && ei->edgeid < conn->num_edges);
+    P4EST_ASSERT ((size_t) ei->edgeid == ez);
+
+    /* set up edge connection information */
+    for (egz = 0; egz < ei->trees.elem_count; ++egz) {
+      pt = (p4est_topidx_t *) sc_array_index (&ei->trees, egz);
+      et = (int8_t *) sc_array_index (&ei->edges, egz);
+      P4EST_ASSERT (0 <= *pt && *pt < conn->num_trees);
+      P4EST_ASSERT (0 <= *et && *et < P8EST_EDGES);
+      P4EST_ASSERT (conn->tree_to_edge[P8EST_EDGES * *pt + *et]
+                    == ei->edgeid);
+      if (real_edges > 0) {
+        conn->tree_to_edge[P8EST_EDGES * *pt + *et] -= real_edges;
+      }
+      for (j = 0; j < 2; ++j) {
+        enode[j] = conn->tree_to_vertex[P4EST_CHILDREN * *pt
+                                        + p8est_edge_corners[*et][j]];
+      }
+      P4EST_ASSERT (enode[0] != enode[1]);
+      conn->edge_to_tree[ett_offset + egz] = *pt;
+      conn->edge_to_edge[ett_offset + egz] =
+        *et + (enode[0] < enode[1] ? 0 : P8EST_EDGES);
+    }
+    ei->edgeid -= real_edges;
+
+    /* determine if this edge is redundant */
+    for (egz = 0; egz < ei->trees.elem_count; ++egz) {
+      pt = (p4est_topidx_t *) sc_array_index (&ei->trees, egz);
+      et = (int8_t *) sc_array_index (&ei->edges, egz);
+      einfo.iedge = -1;         /* unused */
+      P4EST_EXECUTE_ASSERT_FALSE
+        (p8est_find_edge_transform_internal (conn, *pt, *et, &einfo,
+                                             conn->edge_to_tree +
+                                             ett_offset,
+                                             conn->edge_to_edge +
+                                             ett_offset,
+                                             ei->trees.elem_count, ntree));
+      if (eta->elem_count == 0) {
+        P4EST_ASSERT (ntree[0] != -1 || ntree[1] != -1);
+      }
+      else {
+        /* edge is non-redundant */
+        P4EST_ASSERT (ei->trees.elem_count == eta->elem_count
+                      + 1 + (ntree[0] != -1) + (ntree[1] != -1));
+        break;
+      }
+    }
+
+    if (eta->elem_count == 0) {
+      /* erase all references to this redundant edge */
+      for (egz = 0; egz < ei->trees.elem_count; ++egz) {
+        pt = (p4est_topidx_t *) sc_array_index (&ei->trees, egz);
+        et = (int8_t *) sc_array_index (&ei->edges, egz);
+        P4EST_ASSERT (conn->tree_to_edge[P8EST_EDGES * *pt + *et]
+                      == ei->edgeid);
+        conn->tree_to_edge[P8EST_EDGES * *pt + *et] = -1;
+      }
+      ei->edgeid = -1;
+      ++real_edges;
+    }
+    else {
+      /* accept edge as non-redundant */
+      sc_array_reset (eta);
+      conn->ett_offset[ett_edge++] = ett_offset;
+      ett_offset += (p4est_topidx_t) ei->trees.elem_count;
+    }
+  }
+  sc_array_reset (&edge_to_pz);
+  P4EST_ASSERT (ett_edge == conn->num_edges - real_edges);
+  P4EST_ASSERT (ett_offset <= ett_count);
+  conn->ett_offset[ett_edge] = ett_offset;
+  if (real_edges > 0) {
+    conn->num_edges -= real_edges;
+    conn->ett_offset = P4EST_REALLOC (conn->ett_offset, p4est_topidx_t,
+                                      conn->num_edges + 1);
+    conn->edge_to_tree =
+      P4EST_REALLOC (conn->edge_to_tree, p4est_topidx_t, ett_offset);
+    conn->edge_to_edge =
+      P4EST_REALLOC (conn->edge_to_edge, int8_t, ett_offset);
+  }
+  /* clean up storage for all edges */
+  for (ez = 0; ez < edge_array.elem_count; ++ez) {
+    ei = (p8est_conn_edge_info_t *) sc_array_index (&edge_array, ez);
+    P4EST_ASSERT (ei->trees.elem_count >= 1);
+    P4EST_ASSERT (ei->trees.elem_count == ei->edges.elem_count);
+    P4EST_ASSERT (-1 <= ei->edgeid && ei->edgeid < conn->num_edges);
+#ifdef P4EST_ENABLE_DEBUG
+    if (ei->trees.elem_count == 1) {
+      /* isolated edge does not count */
+      P4EST_ASSERT (ei->edgeid == -1);
+      pt = (p4est_topidx_t *) sc_array_index (&ei->trees, 0);
+      et = (int8_t *) sc_array_index (&ei->edges, 0);
+      P4EST_ASSERT (0 <= *pt && *pt < conn->num_trees);
+      P4EST_ASSERT (0 <= *et && *et < P8EST_EDGES);
+      P4EST_ASSERT (conn->tree_to_edge[P8EST_EDGES * *pt + *et] == -1);
+    }
+#endif
+    sc_array_reset (&ei->trees);
+    sc_array_reset (&ei->edges);
+  }
+  sc_array_reset (&edge_array);
+#endif /* P4_TO_P8 */
+
+  /* complete corner identification */
+  P4EST_ASSERT (real_corners == 0);
+  conn->num_corners = conn->num_vertices;
+  conn->ctt_offset = P4EST_ALLOC (p4est_topidx_t, conn->num_corners + 1);
+  conn->corner_to_tree = P4EST_ALLOC (p4est_topidx_t, ctt_count);
+  conn->corner_to_corner = P4EST_ALLOC (int8_t, ctt_count);
+  ctt_corner = 0;
+  ctt_offset = 0;
+  for (nodeid = 0; nodeid < conn->num_vertices; ++nodeid) {
+    nt = node_trees + nodeid;
+    nc = node_corners + nodeid;
+    zcount = nt->elem_count;
+    P4EST_ASSERT (zcount == nc->elem_count);
+    if (zcount <= 1) {
+      /* isolated corner does not count */
+      if (zcount == 1) {
+        pt = (p4est_topidx_t *) sc_array_index (nt, 0);
+        ct = (int8_t *) sc_array_index (nc, 0);
+        P4EST_ASSERT (0 <= *pt && *pt < conn->num_trees);
+        P4EST_ASSERT (0 <= *ct && *ct < P4EST_CHILDREN);
+        P4EST_ASSERT (conn->tree_to_corner[P4EST_CHILDREN * *pt + *ct] ==
+                      nodeid);
+        conn->tree_to_corner[P4EST_CHILDREN * *pt + *ct] = -1;
+      }
+      ++real_corners;
+    }
+    else {
+      /* set up corner connection information */
+      for (pz = 0; pz < zcount; ++pz) {
+        pt = (p4est_topidx_t *) sc_array_index (nt, pz);
+        ct = (int8_t *) sc_array_index (nc, pz);
+        P4EST_ASSERT (0 <= *pt && *pt < conn->num_trees);
+        P4EST_ASSERT (0 <= *ct && *ct < P4EST_CHILDREN);
+        P4EST_ASSERT (conn->tree_to_corner[P4EST_CHILDREN * *pt + *ct]
+                      == nodeid);
+        if (real_corners > 0) {
+          conn->tree_to_corner[P4EST_CHILDREN * *pt + *ct] -= real_corners;
+        }
+        conn->corner_to_tree[ctt_offset + pz] = *pt;
+        conn->corner_to_corner[ctt_offset + pz] = *ct;
+      }
+
+      /* determine if this corner is redundant */
+      for (pz = 0; pz < zcount; ++pz) {
+        pt = (p4est_topidx_t *) sc_array_index (nt, pz);
+        ct = (int8_t *) sc_array_index (nc, pz);
+        cinfo.icorner = -1;     /* unused */
+        ignored =
+          p4est_find_corner_transform_internal (conn, *pt, *ct, &cinfo,
+                                                conn->corner_to_tree +
+                                                ctt_offset,
+                                                conn->corner_to_corner +
+                                                ctt_offset, zcount, ntree);
+#ifndef P4_TO_P8
+        P4EST_ASSERT (ignored == 0);
+#endif
+        if (cta->elem_count == 0) {
+#ifndef P4_TO_P8
+          P4EST_ASSERT (ntree[0] != -1 || ntree[1] != -1);
+#else
+          P4EST_ASSERT (ntree[0] != -1 || ntree[1] != -1 || ntree[2] != -1);
+#endif
+        }
+        else {
+          /* corner is non-redundant */
+#ifdef P4EST_ENABLE_DEBUG
+          expected_count =
+            cta->elem_count + 1 + (ntree[0] != -1) + (ntree[1] != -1);
+#ifdef P4_TO_P8
+          expected_count += (ntree[2] != -1);
+#endif
+          P4EST_ASSERT (zcount == expected_count + ignored);
+#endif
+          break;
+        }
+      }
+
+      if (cta->elem_count == 0) {
+        /* erase all references to this redundant corner */
+        for (pz = 0; pz < zcount; ++pz) {
+          pt = (p4est_topidx_t *) sc_array_index (nt, pz);
+          ct = (int8_t *) sc_array_index (nc, pz);
+          P4EST_ASSERT (conn->tree_to_corner[P4EST_CHILDREN * *pt + *ct]
+                        == nodeid - real_corners);
+          conn->tree_to_corner[P4EST_CHILDREN * *pt + *ct] = -1;
+        }
+        ++real_corners;
+      }
+      else {
+        /* accept corner as non-redundant */
+        sc_array_reset (cta);
+        conn->ctt_offset[ctt_corner++] = ctt_offset;
+        ctt_offset += (p4est_topidx_t) zcount;
+      }
+    }
+  }
+  P4EST_ASSERT (ctt_corner == conn->num_corners - real_corners);
+  P4EST_ASSERT (ctt_offset <= ctt_count);
+  conn->ctt_offset[ctt_corner] = ctt_offset;
+  if (real_corners > 0) {
+    conn->num_corners -= real_corners;
+    conn->ctt_offset = P4EST_REALLOC (conn->ctt_offset, p4est_topidx_t,
+                                      conn->num_corners + 1);
+    conn->corner_to_tree =
+      P4EST_REALLOC (conn->corner_to_tree, p4est_topidx_t, ctt_offset);
+    conn->corner_to_corner =
+      P4EST_REALLOC (conn->corner_to_corner, int8_t, ctt_offset);
+  }
+  /* clean up storage for all corners */
+  for (nodeid = 0; nodeid < conn->num_vertices; ++nodeid) {
+    sc_array_reset (node_trees + nodeid);
+    sc_array_reset (node_corners + nodeid);
+  }
+  P4EST_FREE (node_trees);
+  P4EST_FREE (node_corners);
+
+  /* and be done */
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+}
+
+void
+p4est_connectivity_reduce (p4est_connectivity_t * conn)
+{
+  conn->num_corners = 0;
+  conn->ctt_offset[conn->num_corners] = 0;
+  P4EST_FREE (conn->tree_to_corner);
+  P4EST_FREE (conn->corner_to_tree);
+  P4EST_FREE (conn->corner_to_corner);
+  conn->tree_to_corner = NULL;
+  conn->corner_to_tree = NULL;
+  conn->corner_to_corner = NULL;
+#ifdef P4_TO_P8
+  conn->num_edges = 0;
+  conn->ett_offset[conn->num_edges] = 0;
+  P4EST_FREE (conn->tree_to_edge);
+  P4EST_FREE (conn->edge_to_tree);
+  P4EST_FREE (conn->edge_to_edge);
+  conn->tree_to_edge = NULL;
+  conn->edge_to_tree = NULL;
+  conn->edge_to_edge = NULL;
+#endif
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+}
+
+void
+p4est_connectivity_permute (p4est_connectivity_t * conn, sc_array_t * inperm,
+                            int is_current_to_new)
+{
+  sc_array_t         *permarray;
+  size_t             *perm;
+  p4est_topidx_t      ti, ntrees = conn->num_trees;
+  p4est_topidx_t      tj, count;
+  sc_array_t          array_view;
+  int                 j;
+
+  /* we want the permutation to be the current to new map, not
+   * the new to current map */
+  if (is_current_to_new) {
+    permarray = inperm;
+    perm = (size_t *) permarray->array;
+  }
+  else {
+    permarray = sc_array_new_size (sizeof (size_t), (size_t) ntrees);
+    perm = (size_t *) permarray->array;
+    for (ti = 0; ti < ntrees; ti++) {
+      size_t              mapti = *((size_t *) sc_array_index (inperm, ti));
+      P4EST_ASSERT (mapti < (size_t) ntrees);
+      perm[mapti] = (size_t) ti;
+    }
+  }
+
+  /* first we change the entries in the various tables */
+
+  /* tree_to_tree */
+  for (ti = 0; ti < ntrees; ti++) {
+    for (j = 0; j < P4EST_FACES; j++) {
+      tj = conn->tree_to_tree[P4EST_FACES * ti + j];
+      conn->tree_to_tree[P4EST_FACES * ti + j] = (p4est_topidx_t) perm[tj];
+    }
+  }
+
+#ifdef P4_TO_P8
+  /* edge_to_tree */
+  if (conn->edge_to_tree != NULL) {
+    count = conn->ett_offset[conn->num_edges];
+    for (ti = 0; ti < count; ti++) {
+      tj = conn->edge_to_tree[ti];
+      conn->edge_to_tree[ti] = (p4est_topidx_t) perm[tj];
+    }
+  }
+#endif
+
+  /* corner_to_tree */
+  if (conn->corner_to_tree != NULL) {
+    count = conn->ctt_offset[conn->num_corners];
+    for (ti = 0; ti < count; ti++) {
+      tj = conn->corner_to_tree[ti];
+      conn->corner_to_tree[ti] = (p4est_topidx_t) perm[tj];
+    }
+  }
+
+  /* now we reorder the various tables via in-place permutation */
+
+  /* tree_to_vertex */
+  sc_array_init_data (&array_view, conn->tree_to_vertex,
+                      P4EST_CHILDREN * sizeof (p4est_topidx_t), ntrees);
+  sc_array_permute (&array_view, permarray, 1);
+
+  /* tree_to_tree */
+  sc_array_init_data (&array_view, conn->tree_to_tree,
+                      P4EST_FACES * sizeof (p4est_topidx_t), ntrees);
+  sc_array_permute (&array_view, permarray, 1);
+
+  /* tree_to_face */
+  sc_array_init_data (&array_view, conn->tree_to_face,
+                      P4EST_FACES * sizeof (int8_t), ntrees);
+  sc_array_permute (&array_view, permarray, 1);
+
+#ifdef P4_TO_P8
+  /* tree_to_edge */
+  if (conn->tree_to_edge != NULL) {
+    sc_array_init_data (&array_view, conn->tree_to_edge,
+                        P8EST_EDGES * sizeof (p4est_topidx_t), ntrees);
+    sc_array_permute (&array_view, permarray, 1);
+  }
+#endif
+
+  /* tree_to_corner */
+  if (conn->tree_to_corner != NULL) {
+    sc_array_init_data (&array_view, conn->tree_to_corner,
+                        P4EST_CHILDREN * sizeof (p4est_topidx_t), ntrees);
+    sc_array_permute (&array_view, permarray, 1);
+  }
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+
+  if (!is_current_to_new) {
+    sc_array_destroy (permarray);
+  }
+}
+
+#ifdef P4EST_WITH_METIS
+static int
+reorder_comp (const void *a, const void *b)
+{
+  const int          *A = (const int *) a;
+  const int          *B = (const int *) b;
+
+  if (A[0] < B[0]) {
+    return -1;
+  }
+  else if (B[0] < A[0]) {
+    return 1;
+  }
+  else {
+    return (A[1] - B[1]);
+  }
+}
+
+void
+p4est_connectivity_reorder (MPI_Comm comm, int k, p4est_connectivity_t * conn,
+                            p4est_connect_type_t ctype)
+{
+  int                 n = (int) conn->num_trees;
+  int                *xadj;
+  int                *adjncy;
+  int                *part;
+  int                 totaldeg;
+  int                 degree;
+  int                 i, j, l;
+  int                 rank = -1;
+  p4est_corner_info_t ci;
+  sc_array_t         *cta = &ci.corner_transforms;
+  p4est_corner_transform_t *ct;
+#ifdef P4_TO_P8
+  p8est_edge_info_t   ei;
+  sc_array_t         *eta = &ei.edge_transforms;
+  p8est_edge_transform_t *et;
+#endif
+  int                 volume = -1;
+  size_t              zz;
+  int                 mpiret = MPI_Comm_rank (comm, &rank);
+  sc_array_t         *newid;
+  size_t             *zp;
+  sc_array_t         *sorter;
+  int                *ip;
+  int                 conntype = p4est_connect_type_int (ctype);
+  int                 ncon = 1;
+  int                 success;
+
+  SC_CHECK_MPI (mpiret);
+
+  if (k == 0) {
+    mpiret = MPI_Comm_size (comm, &k);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  P4EST_ASSERT (k > 0);
+
+  /* part will hold the partition number of each tree */
+  part = P4EST_ALLOC (int, n);
+
+  if (!rank) {
+
+    xadj = P4EST_ALLOC (int, n + 1);
+
+    switch (conntype) {
+    case 1:
+      degree = P4EST_FACES;
+      break;
+    case P4EST_DIM:
+      degree = P4EST_INSUL - 1;
+      sc_array_init (cta, sizeof (p4est_corner_transform_t));
+#ifdef P4_TO_P8
+      sc_array_init (eta, sizeof (p8est_edge_transform_t));
+#endif
+      break;
+#ifdef P4_TO_P8
+    case 2:
+      degree = P8EST_FACES + P8EST_EDGES;
+      sc_array_init (eta, sizeof (p8est_edge_transform_t));
+      break;
+#endif
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+
+    if (degree == P4EST_FACES) {
+      /* each tree has the same: metis shouldn't have any trouble with a
+       * loop on a face/edge corner that has no neighbor */
+      for (i = 0; i < n + 1; i++) {
+        xadj[i] = P4EST_FACES * i;
+      }
+      adjncy = P4EST_ALLOC (int, P4EST_FACES * n);
+      for (i = 0; i < n; i++) {
+        for (j = 0; j < P4EST_FACES; j++) {
+          adjncy[P4EST_FACES * i + j] =
+            conn->tree_to_tree[P4EST_FACES * i + j];
+        }
+      }
+    }
+    else {
+      totaldeg = 0;
+      xadj[0] = 0;
+      for (i = 0; i < n; i++) {
+        totaldeg += P4EST_FACES;
+        if (conntype == P4EST_DIM) {
+          for (j = 0; j < P4EST_CHILDREN; j++) {
+            /* add the number of strict corner neighbors */
+            p4est_find_corner_transform (conn, (p4est_topidx_t) i, j, &ci);
+            totaldeg += (int) cta->elem_count;
+          }
+        }
+#ifdef P4_TO_P8
+        if (conntype >= 2) {
+          /* add the number of strict edge neighbors */
+          for (j = 0; j < P8EST_EDGES; j++) {
+            p8est_find_edge_transform (conn, (p4est_topidx_t) i, j, &ei);
+            totaldeg += (int) eta->elem_count;
+          }
+        }
+#endif
+        xadj[i + 1] = totaldeg;
+      }
+
+      adjncy = P4EST_ALLOC (int, totaldeg);
+
+      l = 0;
+      for (i = 0; i < n; i++) {
+        for (j = 0; j < P4EST_FACES; j++) {
+          adjncy[l++] = (int) conn->tree_to_tree[P4EST_FACES * i + j];
+        }
+        if (conntype == P4EST_DIM) {
+          for (j = 0; j < P4EST_CHILDREN; j++) {
+            /* add the number of strict corner neighbors */
+            p4est_find_corner_transform (conn, (p4est_topidx_t) i, j, &ci);
+            for (zz = 0; zz < cta->elem_count; zz++) {
+              ct = p4est_corner_array_index (cta, zz);
+              adjncy[l++] = (int) ct->ntree;
+            }
+          }
+        }
+#ifdef P4_TO_P8
+        if (conntype >= 2) {
+          /* add the number of strict edge neighbors */
+          for (j = 0; j < P8EST_EDGES; j++) {
+            p8est_find_edge_transform (conn, (p4est_topidx_t) i, j, &ei);
+            for (zz = 0; zz < eta->elem_count; zz++) {
+              et = p8est_edge_array_index (eta, zz);
+              adjncy[l++] = (int) et->ntree;
+            }
+          }
+        }
+#endif
+        P4EST_ASSERT (l == xadj[i + 1]);
+      }
+
+      P4EST_ASSERT (l == totaldeg);
+
+      if (conntype == P4EST_DIM) {
+        sc_array_reset (cta);
+      }
+#ifdef P4_TO_P8
+      if (conntype >= 2) {
+        sc_array_reset (eta);
+      }
+#endif
+    }
+
+    P4EST_GLOBAL_INFO ("Entering metis\n");
+    /* now call metis */
+    success = METIS_PartGraphRecursive (&n, &ncon, xadj, adjncy, NULL, NULL,
+                                        NULL, &k, NULL, NULL, NULL, &volume,
+                                        part);
+    P4EST_ASSERT (success == METIS_OK);
+    P4EST_GLOBAL_INFO ("Done metis\n");
+
+    P4EST_GLOBAL_STATISTICSF ("metis volume %d\n", volume);
+
+    P4EST_FREE (xadj);
+    P4EST_FREE (adjncy);
+  }
+
+  /* broadcast part to every process: this is expensive, should probably think
+   * of a better way to do this */
+  MPI_Bcast (part, n, MPI_INT, 0, comm);
+
+  /* now that everyone has part, each process computes the renumbering
+   * for itself*/
+  newid = sc_array_new_size (sizeof (size_t), (size_t) n);
+  sorter = sc_array_new_size (2 * sizeof (int), (size_t) n);
+  for (i = 0; i < n; i++) {
+    ip = (int *) sc_array_index (sorter, i);
+    ip[0] = part[i];
+    ip[1] = i;
+  }
+  P4EST_FREE (part);
+
+  /* sort current index by partition given */
+  /* this will be the same on every process because the comparison operation
+   * does not allow equality between different trees */
+  sc_array_sort (sorter, reorder_comp);
+  for (i = 0; i < n; i++) {
+    ip = (int *) sc_array_index (sorter, i);
+    zp = (size_t *) sc_array_index (newid, ip[1]);
+    *zp = i;
+  }
+  sc_array_destroy (sorter);
+
+  p4est_connectivity_permute (conn, newid, 1);
+
+  sc_array_destroy (newid);
+}
+
+#endif /* P4EST_WITH_METIS */
+
+static int
+p4est_topidx_compare_2 (const void *A, const void *B)
+{
+  int                 ret = p4est_topidx_compare (A, B);
+
+  if (!ret) {
+    const p4est_topidx_t *a = (const p4est_topidx_t *) A;
+    const p4est_topidx_t *b = (const p4est_topidx_t *) B;
+    p4est_topidx_t      diff = a[1] - b[1];
+
+    ret = diff ? (diff < 0 ? -1 : 1) : 0;
+  }
+  return ret;
+}
+
+static void
+p4est_connectivity_store_corner (p4est_connectivity_t * conn,
+                                 p4est_topidx_t t, int c)
+{
+  p4est_topidx_t      n = ++conn->num_corners;
+  p4est_topidx_t     *tc;
+  size_t              zz;
+  size_t              zk;
+  int                 i;
+  sc_array_t         *corner_to_tc;
+
+  P4EST_ASSERT (conn->tree_to_corner == NULL ||
+                conn->tree_to_corner[P4EST_CHILDREN * t + c] < 0);
+
+  conn->ctt_offset = P4EST_REALLOC (conn->ctt_offset, p4est_topidx_t, n + 1);
+  conn->ctt_offset[n] = conn->ctt_offset[n - 1];
+
+  if (conn->tree_to_corner == NULL) {
+    conn->tree_to_corner =
+      P4EST_ALLOC (p4est_topidx_t, P4EST_CHILDREN * conn->num_trees);
+    memset (conn->tree_to_corner, -1,
+            P4EST_CHILDREN * conn->num_trees * sizeof (p4est_topidx_t));
+  }
+
+  corner_to_tc = sc_array_new (2 * sizeof (p4est_topidx_t));
+
+  conn->tree_to_corner[P4EST_CHILDREN * t + c] = n - 1;
+  tc = (p4est_topidx_t *) sc_array_push (corner_to_tc);
+  tc[0] = t;
+  tc[1] = c;
+
+  for (i = 0; i < P4EST_DIM; i++) {
+    int                 f = p4est_corner_faces[c][i];
+    p4est_topidx_t      nt = conn->tree_to_tree[P4EST_FACES * t + f];
+    int                 nf = conn->tree_to_face[P4EST_FACES * t + f];
+    int                 o;
+    int                 nc;
+
+    o = nf / P4EST_FACES;
+    nf %= P4EST_FACES;
+
+    if (nt == t && nf == f) {
+      continue;
+    }
+
+    nc = p4est_connectivity_face_neighbor_corner_orientation (c, f, nf, o);
+
+    conn->tree_to_corner[P4EST_CHILDREN * nt + nc] = n - 1;
+    tc = (p4est_topidx_t *) sc_array_push (corner_to_tc);
+    tc[0] = nt;
+    tc[1] = nc;
+  }
+#ifdef P4_TO_P8
+  for (i = 0; i < P4EST_DIM; i++) {
+    p8est_edge_info_t   ei;
+    p8est_edge_transform_t *et;
+    int                 e = p8est_corner_edges[c][i];
+
+    sc_array_init (&(ei.edge_transforms), sizeof (p8est_edge_transform_t));
+    p8est_find_edge_transform (conn, t, e, &ei);
+
+    for (zz = 0; zz < ei.edge_transforms.elem_count; zz++) {
+      p4est_topidx_t      nt;
+      int                 ne;
+      int                 nc;
+
+      et = p8est_edge_array_index (&(ei.edge_transforms), zz);
+
+      nt = et->ntree;
+      ne = et->nedge;
+      if (p8est_edge_corners[e][0] == c) {
+        nc = p8est_edge_corners[ne][et->nflip];
+      }
+      else {
+        nc = p8est_edge_corners[ne][1 ^ et->nflip];
+      }
+
+      conn->tree_to_corner[P4EST_CHILDREN * nt + nc] = n - 1;
+      tc = (p4est_topidx_t *) sc_array_push (corner_to_tc);
+      tc[0] = nt;
+      tc[1] = nc;
+    }
+
+    sc_array_reset (&(ei.edge_transforms));
+  }
+#endif
+
+  sc_array_sort (corner_to_tc, p4est_topidx_compare_2);
+  sc_array_uniq (corner_to_tc, p4est_topidx_compare_2);
+
+  zk = corner_to_tc->elem_count;
+  conn->ctt_offset[n] += (p4est_topidx_t) zk;
+  conn->corner_to_tree = P4EST_REALLOC (conn->corner_to_tree,
+                                        p4est_topidx_t, conn->ctt_offset[n]);
+  conn->corner_to_corner = P4EST_REALLOC (conn->corner_to_corner,
+                                          int8_t, conn->ctt_offset[n]);
+
+  for (zz = 0; zz < zk; zz++) {
+    tc = (p4est_topidx_t *) sc_array_index (corner_to_tc, zz);
+    conn->corner_to_tree[conn->ctt_offset[n - 1] + zz] = tc[0];
+    conn->corner_to_corner[conn->ctt_offset[n - 1] + zz] = (int8_t) tc[1];
+  }
+
+  sc_array_destroy (corner_to_tc);
+}
+
+#ifdef P4_TO_P8
+static void
+p8est_connectivity_store_edge (p4est_connectivity_t * conn, p4est_topidx_t t,
+                               int e)
+{
+  p4est_topidx_t      n = ++conn->num_edges;
+  p4est_topidx_t     *te;
+  size_t              zz;
+  size_t              zk;
+  int                 i;
+  sc_array_t         *edge_to_te;
+
+  P4EST_ASSERT (conn->tree_to_edge == NULL ||
+                conn->tree_to_edge[P8EST_EDGES * t + e] < 0);
+
+  conn->ett_offset = P4EST_REALLOC (conn->ett_offset, p4est_topidx_t, n + 1);
+  conn->ett_offset[n] = conn->ett_offset[n - 1];
+
+  if (conn->tree_to_edge == NULL) {
+    conn->tree_to_edge =
+      P4EST_ALLOC (p4est_topidx_t, P8EST_EDGES * conn->num_trees);
+    memset (conn->tree_to_edge, -1,
+            P8EST_EDGES * conn->num_trees * sizeof (p4est_topidx_t));
+  }
+
+  edge_to_te = sc_array_new (2 * sizeof (p4est_topidx_t));
+
+  conn->tree_to_edge[P8EST_EDGES * t + e] = n - 1;
+  te = (p4est_topidx_t *) sc_array_push (edge_to_te);
+  te[0] = t;
+  te[1] = e;
+
+  for (i = 0; i < 2; i++) {
+    int                 f = p8est_edge_faces[e][i];
+    p4est_topidx_t      nt = conn->tree_to_tree[P4EST_FACES * t + f];
+    int                 nf = conn->tree_to_face[P4EST_FACES * t + f];
+    int                 o, c[2], nc[2];
+    int                 ne;
+    int                 ref;
+    int                 set;
+    int                 j;
+    int                 diff;
+
+    o = nf / P4EST_FACES;
+    nf %= P4EST_FACES;
+
+    if (t == nt && f == nf) {
+      continue;
+    }
+    ref = p8est_face_permutation_refs[f][nf];
+    set = p8est_face_permutation_sets[ref][o];
+
+    for (j = 0; j < 2; j++) {
+      c[j] = p8est_edge_corners[e][j];
+      nc[j] = p8est_connectivity_face_neighbor_corner_set (c[j], f, nf, set);
+    }
+    diff = SC_MAX (nc[0], nc[1]) - SC_MIN (nc[0], nc[1]);
+    switch (diff) {
+    case 1:
+      ne = p8est_corner_edges[nc[0]][0];
+      break;
+    case 2:
+      ne = p8est_corner_edges[nc[0]][1];
+      break;
+    case 4:
+      ne = p8est_corner_edges[nc[0]][2];
+      break;
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+    conn->tree_to_edge[P8EST_EDGES * nt + ne] = n - 1;
+    if (p8est_edge_corners[ne][0] != nc[0]) {
+      ne += 12;
+    }
+
+    te = (p4est_topidx_t *) sc_array_push (edge_to_te);
+    te[0] = nt;
+    te[1] = ne;
+  }
+
+  sc_array_sort (edge_to_te, p4est_topidx_compare_2);
+  sc_array_uniq (edge_to_te, p4est_topidx_compare_2);
+
+  zk = edge_to_te->elem_count;
+  conn->ett_offset[n] += (p4est_topidx_t) zk;
+  conn->edge_to_tree = P4EST_REALLOC (conn->edge_to_tree,
+                                      p4est_topidx_t, conn->ett_offset[n]);
+  conn->edge_to_edge = P4EST_REALLOC (conn->edge_to_edge,
+                                      int8_t, conn->ett_offset[n]);
+
+  for (zz = 0; zz < zk; zz++) {
+    te = (p4est_topidx_t *) sc_array_index (edge_to_te, zz);
+    conn->edge_to_tree[conn->ett_offset[n - 1] + zz] = te[0];
+    conn->edge_to_edge[conn->ett_offset[n - 1] + zz] = (int8_t) te[1];
+  }
+
+  sc_array_destroy (edge_to_te);
+}
+#endif
+
+static void
+p4est_connectivity_join_corners (p4est_connectivity_t * conn,
+                                 p4est_topidx_t tree_left,
+                                 p4est_topidx_t tree_right,
+                                 int corner_left, int corner_right)
+{
+  p4est_topidx_t      c, c0, c1, swap;
+  p4est_topidx_t      startt, endt, n1, it, end0;
+  p4est_topidx_t     *swapspace;
+  int8_t             *swapspacei;
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+  P4EST_ASSERT (tree_left >= 0 && tree_left < conn->num_trees);
+  P4EST_ASSERT (tree_right >= 0 && tree_right < conn->num_trees);
+  P4EST_ASSERT (corner_left >= 0 && corner_left < P4EST_CHILDREN);
+  P4EST_ASSERT (corner_right >= 0 && corner_right < P4EST_CHILDREN);
+
+  /* it could be that the current connectivity did not store corner information,
+   * because all of the corners are simple enough that they can be figured out
+   * from context.  To simplify things, we're going to only deal with
+   * explicitly stored corners. */
+  if (conn->tree_to_corner == NULL ||
+      conn->tree_to_corner[P4EST_CHILDREN * tree_left + corner_left] < 0) {
+    p4est_connectivity_store_corner (conn, tree_left, corner_left);
+  }
+  if (conn->tree_to_corner == NULL ||
+      conn->tree_to_corner[P4EST_CHILDREN * tree_right + corner_right] < 0) {
+    p4est_connectivity_store_corner (conn, tree_right, corner_right);
+  }
+
+  /* now we know that the two corners are explicitly stored, so it's just a
+   * matter of combining their storage and removing references to one of them
+   * */
+  c0 = conn->tree_to_corner[P4EST_CHILDREN * tree_left + corner_left];
+  c1 = conn->tree_to_corner[P4EST_CHILDREN * tree_right + corner_right];
+
+  if (c0 == c1) {
+    /* whoops, these two corners were already the same */
+    return;
+  }
+  if (c1 < c0) {
+    swap = c0;
+    c0 = c1;
+    c1 = swap;
+  }
+
+  /* remove all reference to c1 */
+  startt = conn->ctt_offset[c1];
+  endt = conn->ctt_offset[c1 + 1];
+
+  n1 = endt - startt;           /* the number of tree corners that border c1 */
+  for (it = startt; it < endt; it++) {  /* get all trees that reference c1 */
+    p4est_topidx_t      nt = conn->corner_to_tree[it];  /* nt is a tree the borders c1 */
+    int                 ntc = (int) conn->corner_to_corner[it]; /* ntc is nt's numering for c1 */
+
+    conn->tree_to_corner[P4EST_CHILDREN * nt + ntc] = c0;       /* c1->c0 */
+  }
+
+  /* we now have to move the entries in corner_to_tree and corner_to_corner around */
+  end0 = conn->ctt_offset[c0 + 1];
+
+  swapspace = P4EST_ALLOC (p4est_topidx_t, n1);
+  memcpy (swapspace, conn->corner_to_tree + (size_t) startt,
+          n1 * sizeof (p4est_topidx_t));
+  memmove (conn->corner_to_tree + (size_t) (end0 + n1),
+           conn->corner_to_tree + (size_t) end0,
+           (size_t) (startt - end0) * sizeof (p4est_topidx_t));
+  memcpy (conn->corner_to_tree + (size_t) end0, swapspace,
+          n1 * sizeof (p4est_topidx_t));
+  P4EST_FREE (swapspace);
+
+  swapspacei = P4EST_ALLOC (int8_t, n1);
+  memcpy (swapspacei, conn->corner_to_corner + (size_t) startt,
+          n1 * sizeof (int8_t));
+  memmove (conn->corner_to_corner + (size_t) (end0 + n1),
+           conn->corner_to_corner + (size_t) end0,
+           (size_t) (startt - end0) * sizeof (int8_t));
+  memcpy (conn->corner_to_corner + (size_t) end0, swapspacei,
+          n1 * sizeof (int8_t));
+  P4EST_FREE (swapspacei);
+
+  /* finally, we have to correct ctt_offset */
+  for (c = c0 + 1; c <= c1; c++) {
+    conn->ctt_offset[c] += n1;
+  }
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+}
+
+#ifdef P4_TO_P8
+static void
+p8est_connectivity_join_edges (p8est_connectivity_t * conn,
+                               p4est_topidx_t tree_left,
+                               p4est_topidx_t tree_right,
+                               int edge_left, int edge_right, int orientation)
+{
+  int                 i, c_left, c_right;
+  p4est_topidx_t      e, e0, e1, swap;
+  p4est_topidx_t      startt, endt, n1, it, end0;
+  p4est_topidx_t     *swapspace;
+  int8_t             *swapspacei;
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+  P4EST_ASSERT (tree_left >= 0 && tree_left < conn->num_trees);
+  P4EST_ASSERT (tree_right >= 0 && tree_right < conn->num_trees);
+  P4EST_ASSERT (edge_left >= 0 && edge_left < P8EST_EDGES);
+  P4EST_ASSERT (edge_right >= 0 && edge_right < P8EST_EDGES);
+  for (i = 0; i < 2; i++) {
+    /* get matching corners */
+    c_left = p8est_edge_corners[edge_left][i];
+    if (orientation) {
+      c_right = p8est_edge_corners[edge_right][1 ^ i];
+    }
+    else {
+      c_right = p8est_edge_corners[edge_right][i];
+    }
+    /* join the corners */
+    p4est_connectivity_join_corners (conn, tree_left, tree_right,
+                                     c_left, c_right);
+  }
+
+  /* it could be that the current connectivity did not store edge information,
+   * because all of the edges are simple enough that they can be figured out
+   * from context.  To simplify things, we're going to only deal with
+   * explicitly stored edges. */
+  if (conn->tree_to_edge == NULL ||
+      conn->tree_to_edge[P8EST_EDGES * tree_left + edge_left] < 0) {
+    p8est_connectivity_store_edge (conn, tree_left, edge_left);
+  }
+  if (conn->tree_to_edge == NULL ||
+      conn->tree_to_edge[P8EST_EDGES * tree_right + edge_right] < 0) {
+    p8est_connectivity_store_edge (conn, tree_right, edge_right);
+  }
+
+  /* now we know that the two edges are explicitly stored, so it's just a
+   * matter of combining their storage and removing references to one of them
+   * */
+  e0 = conn->tree_to_edge[P8EST_EDGES * tree_left + edge_left];
+  e1 = conn->tree_to_edge[P8EST_EDGES * tree_right + edge_right];
+
+  if (e0 == e1) {
+    /* whoops, these two edges were already the same, looks like we did a bunch
+     * of work for nothing */
+    return;
+  }
+  if (e1 < e0) {
+    swap = e0;
+    e0 = e1;
+    e1 = swap;
+  }
+
+  /* remove all reference to e1 */
+  startt = conn->ett_offset[e1];
+  endt = conn->ett_offset[e1 + 1];
+
+  n1 = endt - startt;           /* the number of tree edges that border e1 */
+  for (it = startt; it < endt; it++) {  /* get all trees that reference e1 */
+    p4est_topidx_t      nt = conn->edge_to_tree[it];    /* nt is a tree the borders e1 */
+    int                 nte = (int) conn->edge_to_edge[it];     /* nte is nt's numering for e1,
+                                                                   modified by orientation */
+    int                 o = nte / P8EST_EDGES;  /* o is that modifying orientation */
+
+    nte %= P8EST_EDGES;         /* okay, now nte is nt's numering for e1 */
+    conn->tree_to_edge[P8EST_EDGES * nt + nte] = e0;    /* e1->e0 */
+    /* if edge_left and edge_right have opposite orientations, then the
+     * orientation information in edge_to_edge has to be toggled */
+    conn->edge_to_edge[it] = P8EST_EDGES * (o ^ orientation) + nte;
+  }
+
+  /* we now have to move the entries in edge_to_tree and edge_to_edge around */
+  end0 = conn->ett_offset[e0 + 1];
+
+  swapspace = P4EST_ALLOC (p4est_topidx_t, n1);
+  memcpy (swapspace, conn->edge_to_tree + (size_t) startt,
+          n1 * sizeof (p4est_topidx_t));
+  memmove (conn->edge_to_tree + (size_t) (end0 + n1),
+           conn->edge_to_tree + (size_t) end0,
+           (size_t) (startt - end0) * sizeof (p4est_topidx_t));
+  memcpy (conn->edge_to_tree + (size_t) end0, swapspace,
+          n1 * sizeof (p4est_topidx_t));
+  P4EST_FREE (swapspace);
+
+  swapspacei = P4EST_ALLOC (int8_t, n1);
+  memcpy (swapspacei, conn->edge_to_edge + (size_t) startt,
+          n1 * sizeof (int8_t));
+  memmove (conn->edge_to_edge + (size_t) (end0 + n1),
+           conn->edge_to_edge + (size_t) end0,
+           (size_t) (startt - end0) * sizeof (int8_t));
+  memcpy (conn->edge_to_edge + (size_t) end0, swapspacei,
+          n1 * sizeof (int8_t));
+  P4EST_FREE (swapspacei);
+
+  /* finally, we have to correct ett_offset */
+  for (e = e0 + 1; e <= e1; e++) {
+    conn->ett_offset[e] += n1;
+  }
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+}
+#endif
+
+void
+p4est_connectivity_join_faces (p4est_connectivity_t * conn,
+                               p4est_topidx_t tree_left,
+                               p4est_topidx_t tree_right,
+                               int face_left, int face_right, int orientation)
+{
+#ifdef P4_TO_P8
+  int                 ref, set, j;
+#endif
+  int                 i;
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+  P4EST_ASSERT (tree_left >= 0 && tree_left < conn->num_trees);
+  P4EST_ASSERT (tree_right >= 0 && tree_right < conn->num_trees);
+  P4EST_ASSERT (face_left >= 0 && face_left < P4EST_FACES);
+  P4EST_ASSERT (face_right >= 0 && face_right < P4EST_FACES);
+  P4EST_ASSERT (orientation >= 0 && orientation < P4EST_HALF);
+  P4EST_ASSERT (conn->tree_to_tree[P4EST_FACES * tree_left + face_left] ==
+                tree_left);
+  P4EST_ASSERT (conn->tree_to_tree[P4EST_FACES * tree_right + face_right] ==
+                tree_right);
+  P4EST_ASSERT (conn->tree_to_face[P4EST_FACES * tree_left + face_left] ==
+                (int8_t) face_left);
+  P4EST_ASSERT (conn->tree_to_face[P4EST_FACES * tree_right + face_right] ==
+                (int8_t) face_right);
+
+#ifdef P4_TO_P8
+  /* figure out which edges are next to each other */
+  ref = p8est_face_permutation_refs[face_left][face_right];
+  set = p8est_face_permutation_sets[ref][orientation];
+  for (i = 0; i < 4; i++) {
+    int                 c[2], e_left, e_right, e_orient;
+
+    /* get an edge of face_left */
+    e_left = p8est_face_edges[face_left][i];
+
+    for (j = 0; j < 2; j++) {
+      /* get corners of that edge and their numbers seen from face_right */
+      c[j] = p8est_connectivity_face_neighbor_corner_set
+        (p8est_edge_corners[e_left][j], face_left, face_right, set);
+    }
+    /* now from the two corners, we can figure out e_right */
+    e_right = p8est_child_corner_edges[c[0]][c[1]];
+    P4EST_ASSERT (e_right >= 0);
+
+    /* how are e_left and e_right oriented? 0 for same orientation, 1 for
+     * opposite */
+    e_orient = (p8est_edge_corners[e_right][0] == c[1]);
+
+    /* now we have two facing edges and their orientation, so we can join them */
+    /* these routines will also join the corners */
+    p8est_connectivity_join_edges (conn, tree_left, tree_right, e_left,
+                                   e_right, e_orient);
+  }
+#else
+  for (i = 0; i < 2; i++) {
+    int                 c_left, c_right;
+
+    c_left = p4est_face_corners[face_left][i];
+    if (orientation) {          /* if the two faces have opposite orientation */
+      c_right = p4est_face_corners[face_right][1 ^ i];
+    }
+    else {                      /* if the two faces have the same orientation */
+      c_right = p4est_face_corners[face_right][i];
+    }
+
+    /* join the corners */
+    p4est_connectivity_join_corners (conn, tree_left, tree_right, c_left,
+                                     c_right);
+  }
+#endif
+
+  conn->tree_to_tree[P4EST_FACES * tree_left + face_left] = tree_right;
+  conn->tree_to_tree[P4EST_FACES * tree_right + face_right] = tree_left;
+  conn->tree_to_face[P4EST_FACES * tree_left + face_left] =
+    face_right + P4EST_FACES * orientation;
+  conn->tree_to_face[P4EST_FACES * tree_right + face_right] =
+    face_left + P4EST_FACES * orientation;
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+}
+
+#ifdef P4_TO_P8
+static int
+p8est_edge_compare (const void *a, const void *b)
+{
+  const p8est_edge_transform_t *A = (const p8est_edge_transform_t *) a;
+  const p8est_edge_transform_t *B = (const p8est_edge_transform_t *) b;
+
+  return (A->ntree != B->ntree) ? A->ntree - B->ntree :
+    (A->nedge != B->nedge) ? A->nedge - B->nedge :
+    (A->naxis[0] != B->naxis[0]) ? A->naxis[0] - B->naxis[0] :
+    (A->naxis[1] != B->naxis[1]) ? A->naxis[1] - B->naxis[1] :
+    (A->naxis[2] != B->naxis[2]) ? A->naxis[2] - B->naxis[2] :
+    (A->nflip != B->nflip) ? A->nflip - B->nflip : A->corners - B->corners;
+}
+#endif
+
+static int
+p4est_corner_compare (const void *a, const void *b)
+{
+  const p4est_corner_transform_t *A = (const p4est_corner_transform_t *) a;
+  const p4est_corner_transform_t *B = (const p4est_corner_transform_t *) b;
+
+  return (A->ntree != B->ntree) ? A->ntree - B->ntree :
+    A->ncorner - B->ncorner;
+}
+
+int
+p4est_connectivity_is_equivalent (p4est_connectivity_t * conn1,
+                                  p4est_connectivity_t * conn2)
+{
+  size_t              count;
+  p4est_topidx_t      ntrees, t;
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn1));
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn2));
+  size_t              topsize = sizeof (p4est_topidx_t);
+  size_t              int8size = sizeof (int8_t);
+
+  /* same pointer or equality are stronger */
+  if (conn1 == conn2 || p4est_connectivity_is_equal (conn1, conn2)) {
+    return 1;
+  }
+
+  ntrees = conn1->num_trees;
+
+  /* clearly must have same number of trees */
+  if (conn2->num_trees != ntrees) {
+    return 0;
+  }
+
+  /* compare tree_to_tree, tree_to_face structure: must be exactly the same */
+  count = (size_t) (P4EST_FACES * conn1->num_trees);
+  if (memcmp (conn1->tree_to_tree, conn2->tree_to_tree, count * topsize) ||
+      memcmp (conn1->tree_to_face, conn2->tree_to_face, count * int8size)) {
+    return 0;
+  }
+
+  /* test equivalence of edges and corners only through the transforms: the
+   * numbering of the edges and corners is not relevant */
+
+#ifdef P4_TO_P8
+  {
+    p8est_edge_info_t   e1, e2;
+
+    sc_array_init (&e1.edge_transforms, sizeof (p8est_edge_transform_t));
+    sc_array_init (&e2.edge_transforms, sizeof (p8est_edge_transform_t));
+    for (t = 0; t < ntrees; t++) {
+      int                 e;
+      size_t              zz;
+
+      for (e = 0; e < P8EST_EDGES; e++) {
+        p8est_find_edge_transform (conn1, t, e, &e1);
+        p8est_find_edge_transform (conn2, t, e, &e2);
+        if (e1.edge_transforms.elem_count != e2.edge_transforms.elem_count) {
+          return 0;
+        }
+        /* sort so memory comparison is correct */
+        sc_array_sort (&e1.edge_transforms, p8est_edge_compare);
+        sc_array_sort (&e2.edge_transforms, p8est_edge_compare);
+        if (e1.edge_transforms.elem_count != e2.edge_transforms.elem_count) {
+          return 0;
+        }
+        for (zz = 0; zz < e1.edge_transforms.elem_count; zz++) {
+          p8est_edge_transform_t *t1 = p8est_edge_array_index
+            (&e1.edge_transforms, zz);
+          p8est_edge_transform_t *t2 = p8est_edge_array_index
+            (&e2.edge_transforms, zz);
+
+          if (t1->corners != t2->corners ||
+              t1->naxis[0] != t2->naxis[0] ||
+              t1->naxis[1] != t2->naxis[1] ||
+              t1->naxis[2] != t2->naxis[2] ||
+              t1->nedge != t2->nedge ||
+              t1->nflip != t2->nflip || t1->ntree != t2->ntree) {
+            return 0;
+          }
+        }
+      }
+    }
+    sc_array_reset (&e1.edge_transforms);
+    sc_array_reset (&e2.edge_transforms);
+  }
+#endif
+  {
+    p4est_corner_info_t c1, c2;
+
+    sc_array_init (&c1.corner_transforms, sizeof (p4est_corner_transform_t));
+    sc_array_init (&c2.corner_transforms, sizeof (p4est_corner_transform_t));
+    for (t = 0; t < ntrees; t++) {
+      int                 c;
+      size_t              zz;
+
+      for (c = 0; c < P4EST_CHILDREN; c++) {
+        p4est_find_corner_transform (conn1, t, c, &c1);
+        p4est_find_corner_transform (conn2, t, c, &c2);
+        if (c1.corner_transforms.elem_count !=
+            c2.corner_transforms.elem_count) {
+          return 0;
+        }
+        /* sort so memory comparison is correct */
+        sc_array_sort (&c1.corner_transforms, p4est_corner_compare);
+        sc_array_sort (&c2.corner_transforms, p4est_corner_compare);
+
+        if (c1.corner_transforms.elem_count !=
+            c2.corner_transforms.elem_count) {
+          return 0;
+        }
+        for (zz = 0; zz < c1.corner_transforms.elem_count; zz++) {
+          p4est_corner_transform_t *t1 = p4est_corner_array_index
+            (&c1.corner_transforms, zz);
+          p4est_corner_transform_t *t2 = p4est_corner_array_index
+            (&c2.corner_transforms, zz);
+
+          if (t1->ncorner != t2->ncorner || t1->ntree != t2->ntree) {
+            return 0;
+          }
+        }
+      }
+    }
+    sc_array_reset (&c1.corner_transforms);
+    sc_array_reset (&c2.corner_transforms);
+  }
+  return 1;
+}
+
+#ifndef P4_TO_P8
+
+int
+p4est_connect_type_int (p4est_connect_type_t btype)
+{
+  switch (btype) {
+  case P4EST_CONNECT_FACE:
+    return 1;
+  case P4EST_CONNECT_CORNER:
+    return 2;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+}
+
+const char         *
+p4est_connect_type_string (p4est_connect_type_t btype)
+{
+  switch (btype) {
+  case P4EST_CONNECT_FACE:
+    return "FACE";
+  case P4EST_CONNECT_CORNER:
+    return "CORNER";
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+}
+
+#endif /* !P4_TO_P8 */
+/*
+ * Read a line from a file. Obtained from:
+ * http://stackoverflow.com/questions/314401/
+ * how-to-read-a-line-from-the-console-in-c/314422#314422
+ *
+ * Using this avoids a dependence on IEEE Std 1003.1-2008 (``POSIX.1'') for the
+ * getline function.
+ */
+static char        *
+p4est_connectivity_getline_upper (FILE * stream)
+{
+  char               *line = P4EST_ALLOC (char, 1024), *linep = line;
+  size_t              lenmax = 1024, len = lenmax;
+  int                 c;
+
+  if (line == NULL)
+    return NULL;
+
+  for (;;) {
+    c = fgetc (stream);
+    c = toupper (c);
+    if (c == EOF && linep == line) {
+      P4EST_FREE (linep);
+      return NULL;
+    }
+
+    if (--len == 0) {
+      len = lenmax;
+      lenmax *= 2;
+      char               *linen = P4EST_REALLOC (linep, char, lenmax);
+
+      if (linen == NULL) {
+        P4EST_FREE (linep);
+        return NULL;
+      }
+      line = linen + (line - linep);
+      linep = linen;
+    }
+    if ((*line++ = c) == '\n')
+      break;
+  }
+  *line = '\0';
+  return linep;
+}
+
+int
+p4est_connectivity_read_inp_stream (FILE * stream,
+                                    p4est_topidx_t * num_vertices,
+                                    p4est_topidx_t * num_trees,
+                                    double *vertices,
+                                    p4est_topidx_t * tree_to_vertex)
+{
+  int                 reading_nodes = 0, reading_elements = 0;
+  int                 lines_read = 0, lines_free = 0;
+  char               *line;
+  p4est_topidx_t      num_nodes = 0;
+  p4est_topidx_t      num_elements = 0;
+  int                 fill_trees_and_vertices = (vertices != NULL &&
+                                                 tree_to_vertex != NULL);
+
+  P4EST_ASSERT ((vertices == NULL && tree_to_vertex == NULL) ||
+                (vertices != NULL && tree_to_vertex != NULL));
+
+  for (;;) {
+    line = p4est_connectivity_getline_upper (stream);
+
+    if (line == NULL) {
+      break;
+    }
+
+    ++lines_read;
+
+    /* check for control line */
+    if (line[0] == '*') {
+      reading_elements = reading_nodes = 0;
+      if (strstr (line, "*NODE")) {
+        reading_nodes = 1;
+        ++lines_free;
+        P4EST_FREE (line);
+        continue;
+      }
+      else if (strstr (line, "*ELEMENT")) {
+        if (
+#ifdef P4_TO_P8
+             strstr (line, "TYPE=C3D8")
+#else
+             strstr (line, "TYPE=C2D4") || strstr (line, "TYPE=CPS4")
+             || strstr (line, "TYPE=S4")
+#endif
+          ) {
+          reading_elements = 1;
+          ++lines_free;
+          P4EST_FREE (line);
+          continue;
+        }
+      }
+    }
+
+    if (reading_nodes) {
+      if (fill_trees_and_vertices) {
+        long long int       node;
+        double              x, y, z;
+        int                 retval;
+
+        retval = sscanf (line, "%lld, %lf, %lf, %lf", &node, &x, &y, &z);
+        if (retval != 4) {
+          P4EST_LERROR ("Premature end of file");
+          P4EST_FREE (line);
+          return 1;
+        }
+
+        if (node > *num_vertices) {
+          P4EST_LERRORF
+            ("Encountered vertex %lld that will not fit in vertices"
+             " array of length %lld.  Are the vertices contiguously"
+             " numbered?\n", node, (long long int) *num_vertices);
+          P4EST_FREE (line);
+          return 1;
+        }
+
+        vertices[3 * (node - 1) + 0] = x;
+        vertices[3 * (node - 1) + 1] = y;
+        vertices[3 * (node - 1) + 2] = z;
+      }
+
+      ++num_nodes;
+    }
+    else if (reading_elements) {
+      if (fill_trees_and_vertices) {
+        long long int       v[P4EST_CHILDREN];
+        int                 n;
+        int                 retval;
+
+        if (num_elements >= *num_trees) {
+          P4EST_LERROR ("Encountered element that will not fit into"
+                        " tree_to_vertex array. More elements than expected.\n");
+          P4EST_FREE (line);
+          return 1;
+        }
+
+        /* Note that when we read in the
+         * vertices we switch from right-hand
+         * vertex ordering to z-order
+         */
+        retval = sscanf (line, "%*d, %lld, %lld, %lld, %lld"
+#ifdef P4_TO_P8
+                         ", %lld, %lld, %lld, %lld"
+#endif
+                         , &v[0], &v[1], &v[3], &v[2]
+#ifdef P4_TO_P8
+                         , &v[4], &v[5], &v[7], &v[6]
+#endif
+          );
+        if (retval != P4EST_CHILDREN) {
+          P4EST_LERROR ("Premature end of file");
+          P4EST_FREE (line);
+          return 1;
+        }
+
+        for (n = 0; n < P4EST_CHILDREN; ++n)
+          tree_to_vertex[P4EST_CHILDREN * num_elements + n] = v[n] - 1;
+      }
+
+      ++num_elements;
+    }
+
+    ++lines_free;
+    P4EST_FREE (line);
+  }
+
+  *num_vertices = num_nodes;
+  *num_trees = num_elements;
+
+  if (num_nodes == 0 || num_elements == 0) {
+    P4EST_LERROR ("No elements or nodes found in mesh file.\n");
+    return -1;
+  }
+  else {
+    return 0;
+  }
+}
+
+p4est_connectivity_t *
+p4est_connectivity_read_inp (const char *filename)
+{
+  int                 retval;
+  p4est_topidx_t      num_vertices = 0, num_trees = 0, tree;
+  int                 face;
+
+  p4est_connectivity_t *conn = NULL;
+  FILE               *fid = NULL;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Reading connectivity from %s\n", filename);
+
+  fid = fopen (filename, "rb");
+  if (fid == NULL) {
+    P4EST_LERRORF ("Failed to open %s\n", filename);
+    goto dead;
+  }
+
+  if (p4est_connectivity_read_inp_stream
+      (fid, &num_vertices, &num_trees, NULL, NULL)) {
+    P4EST_LERRORF ("Failed to read %s: pass 1\n", filename);
+    goto dead;
+  }
+
+  rewind (fid);
+
+  conn = p4est_connectivity_new (num_vertices, num_trees,
+#ifdef P4_TO_P8
+                                 0, 0,
+#endif
+                                 0, 0);
+
+  if (p4est_connectivity_read_inp_stream (fid, &conn->num_vertices,
+                                          &conn->num_trees, conn->vertices,
+                                          conn->tree_to_vertex)) {
+    P4EST_LERRORF ("Failed to read %s: pass 2\n", filename);
+    goto dead;
+  }
+
+  /*
+   * Fill tree_to_tree and tree_to_face to make sure we have a valid
+   * connectivity.
+   */
+  for (tree = 0; tree < conn->num_trees; ++tree) {
+    for (face = 0; face < P4EST_FACES; ++face) {
+      conn->tree_to_tree[P4EST_FACES * tree + face] = tree;
+      conn->tree_to_face[P4EST_FACES * tree + face] = face;
+    }
+  }
+  P4EST_ASSERT (p4est_connectivity_is_valid (conn));
+
+  /* Compute real tree_to_* fields and complete (edge and) corner fields. */
+  p4est_connectivity_complete (conn);
+
+  retval = fclose (fid);
+  fid = NULL;
+  if (retval) {
+    P4EST_LERRORF ("Failed to close %s\n", filename);
+    goto dead;
+  }
+
+  P4EST_GLOBAL_PRODUCTIONF
+    ("New connectivity with %lld trees and %lld vertices\n",
+     (long long) conn->num_trees, (long long) conn->num_vertices);
+
+  return conn;
+
+dead:
+  /* clean up on error */
+  if (fid != NULL) {
+    fclose (fid);
+  }
+  if (conn != NULL) {
+    p4est_connectivity_destroy (conn);
+  }
+  return NULL;
+}
diff --git a/src/p4est_connectivity.h b/src/p4est_connectivity.h
new file mode 100644
index 0000000..3ce9304
--- /dev/null
+++ b/src/p4est_connectivity.h
@@ -0,0 +1,721 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_connectivity.h
+ *
+ * The coarse topological description of the forest.
+ *
+ * \ingroup p4est
+ */
+
+#ifndef P4EST_CONNECTIVITY_H
+#define P4EST_CONNECTIVITY_H
+
+#ifdef P4_TO_P8
+#error "Including a p4est header with P4_TO_P8 defined"
+#endif
+
+#include <sc_io.h>
+#include <p4est_base.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** The spatial dimension */
+#define P4EST_DIM 2
+/** The number of faces of a quadrant */
+#define P4EST_FACES (2 * P4EST_DIM)
+/** The number of children of a quadrant
+ *
+ * also the nmber of corners */
+#define P4EST_CHILDREN 4
+/** The number of children/corners touching one face */
+#define P4EST_HALF (P4EST_CHILDREN / 2)
+/** The size of insulation layer */
+#define P4EST_INSUL 9
+
+/* size of face transformation encoding */
+#define P4EST_FTRANSFORM 9
+
+/** p4est identification string */
+#define P4EST_STRING "p4est"
+
+/* Increase this number whenever the on-disk format for
+ * p4est_connectivity, p4est, or any other 2D data structure changes.
+ * The format for reading and writing must be the same.
+ */
+#define P4EST_ONDISK_FORMAT 0x2000009
+
+/** Characterize a type of adjacency.
+ *
+ * Several functions involve relationships between neighboring trees and/or
+ * quadrants, and their behavior depends on how one defines adjacency:
+ * 1) entities are adjacent if they share a face, or
+ * 2) entities are adjacent if they share a face or corner.
+ * p4est_connect_type_t is used to choose the desired behavior.
+ * This enum must fit into an int8_t.
+ */
+typedef enum
+{
+  /* make sure to have different values 2D and 3D */
+  P4EST_CONNECT_FACE = 21,
+  P4EST_CONNECT_CORNER = 22,
+  P4EST_CONNECT_FULL = P4EST_CONNECT_CORNER
+}
+p4est_connect_type_t;
+
+#ifdef P4EST_BACKWARD_DEALII
+typedef p4est_connect_type_t p4est_balance_type_t;
+#endif
+
+/** Typedef for serialization method. */
+typedef enum
+{
+  P4EST_CONN_ENCODE_NONE = SC_IO_ENCODE_NONE,
+  P4EST_CONN_ENCODE_LAST        /**< Invalid entry to close the list. */
+}
+p4est_connectivity_encode_t;
+
+/** Convert the p4est_connect_type_t into a number.
+ * \param [in] btype    The balance type to convert.
+ * \return              Returns 1 or 2.
+ */
+int                 p4est_connect_type_int (p4est_connect_type_t btype);
+
+/** Convert the p4est_connect_type_t into a const string.
+ * \param [in] btype    The balance type to convert.
+ * \return              Returns a pointer to a constant string.
+ */
+const char         *p4est_connect_type_string (p4est_connect_type_t btype);
+
+/** This structure holds the 2D inter-tree connectivity information.
+ * Identification of arbitrary faces and corners is possible.
+ *
+ * The arrays tree_to_* are stored in z ordering.
+ * For corners the order wrt. yx is 00 01 10 11.
+ * For faces the order is -x +x -y +y.
+ * They are allocated [0][0]..[0][3]..[num_trees-1][0]..[num_trees-1][3].
+ *
+ * The values for tree_to_face are 0..7
+ * where ttf % 4 gives the face number and ttf / 4 the face orientation code.
+ * The orientation is 0 for edges that are aligned in z-order,
+ * and 1 for edges that are running opposite in z-order.
+ *
+ * It is valid to specify num_vertices as 0.
+ * In this case vertices and tree_to_vertex are set to NULL.
+ * Otherwise the vertex coordinates are stored in the array vertices as
+ * [0][0]..[0][2]..[num_vertices-1][0]..[num_vertices-1][2].
+ *
+ * The corners are only stored when they connect trees.
+ * Otherwise the tree_to_corner entry must be -1 and this corner is ignored.
+ * If num_corners == 0, tree_to_corner and corner_to_* arrays are set to NULL.
+ *
+ * The arrays corner_to_* store a variable number of entries per corner.
+ * For corner c these are at position [ctt_offset[c]]..[ctt_offset[c+1]-1].
+ * Their number for corner c is ctt_offset[c+1] - ctt_offset[c].
+ * The size of the corner_to_* arrays is num_ctt = ctt_offset[num_corners].
+ *
+ * The *_to_attr arrays may have arbitrary contents defined by the user.
+ */
+typedef struct p4est_connectivity
+{
+  p4est_topidx_t      num_vertices; /**< the number of vertices that define
+                                         the \a embedding of the forest (not
+                                         the topology) */
+  p4est_topidx_t      num_trees;    /**< the number of trees */
+  p4est_topidx_t      num_corners;  /**< the number of corners that help
+                                         define topology */
+  double             *vertices;     /**< an array of size
+                                         (3 * \a num_vertices) */
+  p4est_topidx_t     *tree_to_vertex; /**< embed each tree into \f$R^3\f$ for
+                                           e.g. visualization (see
+                                           p4est_vtk.h) */
+
+  size_t              tree_attr_bytes;  /**< bytes per tree in tree_to_attr */
+  char               *tree_to_attr;     /**< not touched by p4est */
+
+  p4est_topidx_t     *tree_to_tree; /**< (4 * \a num_trees) neighbors across
+                                         faces */
+  int8_t             *tree_to_face; /**< (4 * \a num_trees) face to
+                                         face+orientation (see description) */
+
+  p4est_topidx_t     *tree_to_corner; /**< (4 * \a num_trees) or NULL (see
+                                           description) */
+  p4est_topidx_t     *ctt_offset; /**< corner to offset in \a corner_to_tree
+                                       and \a corner_to_corner */
+  p4est_topidx_t     *corner_to_tree; /**< list of trees that meet at a corner */
+  int8_t             *corner_to_corner; /**< list of tree-corners that meet at
+                                             a corner */
+}
+p4est_connectivity_t;
+
+/** Calculate memory usage of a connectivity structure.
+ * \param [in] conn   Connectivity structure.
+ * \return            Memory used in bytes.
+ */
+size_t              p4est_connectivity_memory_used (p4est_connectivity_t *
+                                                    conn);
+
+typedef struct
+{
+  p4est_topidx_t      ntree;
+  int8_t              ncorner;
+}
+p4est_corner_transform_t;
+
+typedef struct
+{
+  p4est_topidx_t      icorner;
+  sc_array_t          corner_transforms;
+}
+p4est_corner_info_t;
+
+/** Store the corner numbers 0..4 for each tree face. */
+extern const int    p4est_face_corners[4][2];
+
+/** Store the face numbers in the face neighbor's system. */
+extern const int    p4est_face_dual[4];
+
+/** Store the face numbers 0..3 for each tree corner. */
+extern const int    p4est_corner_faces[4][2];
+
+/** Store the face corner numbers for the faces touching a tree corner. */
+extern const int    p4est_corner_face_corners[4][4];
+
+/** Store the faces for each child and corner, can be -1. */
+extern const int    p4est_child_corner_faces[4][4];
+
+/** Transform a corner across one of the adjacent faces into a neighbor tree.
+ * This version expects the neighbor face and orientation separately.
+ * \param [in] c    A corner number in 0..3.
+ * \param [in] f    A face number that touches the corner \a c.
+ * \param [in] nf   A neighbor face that is on the other side of \f.
+ * \param [in] o    The orientation between tree boundary faces \a f and \nf.
+ */
+int                 p4est_connectivity_face_neighbor_corner_orientation
+  (int c, int f, int nf, int o);
+
+/** Allocate a connectivity structure.
+ * The attribute fields are initialized to NULL.
+ * \param [in] num_vertices   Number of total vertices (i.e. geometric points).
+ * \param [in] num_trees      Number of trees in the forest.
+ * \param [in] num_corners    Number of tree-connecting corners.
+ * \param [in] num_ctt        Number of total trees in corner_to_tree array.
+ * \return                    A connectivity structure with allocated arrays.
+ */
+p4est_connectivity_t *p4est_connectivity_new (p4est_topidx_t num_vertices,
+                                              p4est_topidx_t num_trees,
+                                              p4est_topidx_t num_corners,
+                                              p4est_topidx_t num_ctt);
+
+/** Allocate a connectivity structure and populate from constants.
+ * The attribute fields are initialized to NULL.
+ * \param [in] num_vertices   Number of total vertices (i.e. geometric points).
+ * \param [in] num_trees      Number of trees in the forest.
+ * \param [in] num_corners    Number of tree-connecting corners.
+ * \param [in] coff           Corner-to-tree offsets (num_corners + 1 values).
+ * \return                    The connectivity is checked for validity.
+ */
+p4est_connectivity_t *p4est_connectivity_new_copy (p4est_topidx_t
+                                                   num_vertices,
+                                                   p4est_topidx_t num_trees,
+                                                   p4est_topidx_t num_corners,
+                                                   const double *vertices,
+                                                   const p4est_topidx_t * ttv,
+                                                   const p4est_topidx_t * ttt,
+                                                   const int8_t * ttf,
+                                                   const p4est_topidx_t * ttc,
+                                                   const p4est_topidx_t *
+                                                   coff,
+                                                   const p4est_topidx_t * ctt,
+                                                   const int8_t * ctc);
+
+/** Destroy a connectivity structure.  Also destroy all attributes.
+ */
+void                p4est_connectivity_destroy (p4est_connectivity_t *
+                                                connectivity);
+
+/** Allocate or free the attribute fields in a connectivity.
+ * \param [in,out] conn         The conn->*_to_attr fields must either be NULL
+ *                              or previously be allocated by this function.
+ * \param [in] bytes_per_tree   If 0, tree_to_attr is freed (being NULL is ok).
+ *                              If positive, requested space is allocated.
+ */
+void                p4est_connectivity_set_attr (p4est_connectivity_t * conn,
+                                                 size_t bytes_per_tree);
+
+/** Examine a connectivity structure.
+ * \return          Returns true if structure is valid, false otherwise.
+ */
+int                 p4est_connectivity_is_valid (p4est_connectivity_t *
+                                                 connectivity);
+
+/** Check two connectivity structures for equality.
+ * \return          Returns true if structures are equal, false otherwise.
+ */
+int                 p4est_connectivity_is_equal (p4est_connectivity_t * conn1,
+                                                 p4est_connectivity_t *
+                                                 conn2);
+
+/** Write connectivity to a sink object.
+ * \param [in] conn     The connectivity to be written.
+ * \param [in,out] sink The connectivity is written into this sink.
+ * \return              0 on success, nonzero on error.
+ */
+int                 p4est_connectivity_sink (p4est_connectivity_t * conn,
+                                             sc_io_sink_t * sink);
+
+/** Allocate memory and store the connectivity information there.
+ * \param [in] conn     The connectivity structure to be exported to memory.
+ * \param [in] code     Encoding and compression method for serialization.
+ * \return              Newly created array that contains the information.
+ */
+sc_array_t         *p4est_connectivity_deflate (p4est_connectivity_t * conn,
+                                                p4est_connectivity_encode_t
+                                                code);
+
+/** Save a connectivity structure to disk.
+ * \param [in] filename         Name of the file to write.
+ * \param [in] connectivity     Valid connectivity structure.
+ * \return                      Returns 0 on success, nonzero on file error.
+ */
+int                 p4est_connectivity_save (const char *filename,
+                                             p4est_connectivity_t *
+                                             connectivity);
+
+/** Read connectivity from a source object.
+ * \param [in,out] source       The connectivity is read from this source.
+ * \return              The newly created connectivity, or NULL on error.
+ */
+p4est_connectivity_t *p4est_connectivity_source (sc_io_source_t * source);
+
+/** Create new connectivity from a memory buffer.
+ * \param [in] buffer   The connectivity is created from this memory buffer.
+ * \return              The newly created connectivity, or NULL on error.
+ */
+p4est_connectivity_t *p4est_connectivity_inflate (sc_array_t * buffer);
+
+/** Load a connectivity structure from disk.
+ * \param [in] filename         Name of the file to read.
+ * \param [in,out] bytes        Size in bytes of connectivity on disk or NULL.
+ * \return              Returns valid connectivity, or NULL on file error.
+ */
+p4est_connectivity_t *p4est_connectivity_load (const char *filename,
+                                               size_t * bytes);
+
+/** Create a connectivity structure for the unit square.
+ */
+p4est_connectivity_t *p4est_connectivity_new_unitsquare (void);
+
+/** Create a connectivity structure for an all-periodic unit square.
+ */
+p4est_connectivity_t *p4est_connectivity_new_periodic (void);
+
+/** Create a connectivity structure for a periodic unit square.
+ * The left and right faces are identified, and bottom and top opposite.
+ */
+p4est_connectivity_t *p4est_connectivity_new_rotwrap (void);
+
+/** Create a connectivity structure for a three-tree mesh around a corner.
+ */
+p4est_connectivity_t *p4est_connectivity_new_corner (void);
+
+/** Create a connectivity structure for two trees on top of each other.
+ */
+p4est_connectivity_t *p4est_connectivity_new_pillow (void);
+
+/** Create a connectivity structure for a five-tree moebius band.
+ */
+p4est_connectivity_t *p4est_connectivity_new_moebius (void);
+
+/** Create a connectivity structure for a six-tree star.
+ */
+p4est_connectivity_t *p4est_connectivity_new_star (void);
+
+/** Create a connectivity structure for the six sides of a unit cube.
+ * The ordering of the trees is as follows: 0 1
+ *                                            2 3 <-- 3: axis-aligned top side
+ *                                              4 5.
+ * This choice has been made for maximum symmetry (see tree_to_* in .c file).
+ */
+p4est_connectivity_t *p4est_connectivity_new_cubed (void);
+
+/** Create a connectivity structure for a five-tree flat spherical disk.
+ * The ordering of the trees is as follows:   4
+ *                                          1 2 3
+ *                                            0.
+ */
+p4est_connectivity_t *p4est_connectivity_new_disk (void);
+
+/** An m by n array with periodicity in x and y if periodic_a and periodic_b
+ * are true, respectively.
+ */
+p4est_connectivity_t *p4est_connectivity_new_brick (int mi, int ni,
+                                                    int periodic_a,
+                                                    int periodic_b);
+
+/** Create connectivity structure from predefined catalogue.
+ * \param [in]  name            Invokes connectivity_new_* function.
+ *              brick23         brick (2, 3, 0, 0)
+ *              corner          corner
+ *              cubed           cubed
+ *              disk            disk
+ *              moebius         moebius
+ *              periodic        periodic
+ *              pillow          pillow
+ *              rotwrap         rotwrap
+ *              star            star
+ *              unit            unitsquare
+ * \return      An initialized connectivity if name is defined, NULL else.
+ */
+p4est_connectivity_t *p4est_connectivity_new_byname (const char *name);
+
+/** Fill an array with the axis combination of a face neighbor transform.
+ * \param [in]  iface       The number of the originating face.
+ * \param [in]  nface       Encoded as nface = r * 4 + nf, where nf = 0..3 is
+ *                          the neigbbor's connecting face number and r = 0..1
+ *                          is the relative orientation to the neighbor's face.
+ *                          This encoding matches p4est_connectivity_t.
+ * \param [out] ftransform  This array holds 9 integers.
+ *              [0,2]       The coordinate axis sequence of the origin face,
+ *                          the first referring to the tangential and the second
+ *                          to the normal.  A permutation of (0, 1).
+ *              [3,5]       The coordinate axis sequence of the target face.
+ *              [6,8]       Edge reversal flag for tangential axis (boolean);
+ *                          face code in [0, 3] for the normal coordinate q:
+ *                          0: q' = -q
+ *                          1: q' = q + 1
+ *                          2: q' = q - 1
+ *                          3: q' = 2 - q
+ *              [1,4,7]     0 (unused for compatibility with 3D).
+ */
+void                p4est_expand_face_transform (int iface, int nface,
+                                                 int ftransform[]);
+
+/** Fill an array with the axis combinations of a tree neighbor transform.
+ * \param [in]  itree       The number of the originating tree.
+ * \param [in]  iface       The number of the originating tree's face.
+ * \param [out] ftransform  This array holds 9 integers.
+ *              [0,2]       The coordinate axis sequence of the origin face.
+ *              [3,5]       The coordinate axis sequence of the target face.
+ *              [6,8]       Edge reverse flag for axis t; face code for axis n.
+ *              [1,4,7]     0 (unused for compatibility with 3D).
+ * \return                  The face neighbor tree if it exists, -1 otherwise.
+ */
+p4est_topidx_t      p4est_find_face_transform (p4est_connectivity_t *
+                                               connectivity,
+                                               p4est_topidx_t itree,
+                                               int iface, int ftransform[]);
+
+/** Fills an array with information about corner neighbors.
+ * \param [in] itree    The number of the originating tree.
+ * \param [in] icorner  The number of the originating corner.
+ * \param [in,out] ci   A p4est_corner_info_t structure with initialized array.
+ */
+void                p4est_find_corner_transform (p4est_connectivity_t *
+                                                 connectivity,
+                                                 p4est_topidx_t itree,
+                                                 int icorner,
+                                                 p4est_corner_info_t * ci);
+
+/** Internally connect a connectivity based on tree_to_vertex information.
+ * Periodicity that is not inherent in the list of vertices will be lost.
+ * \param [in,out] conn     The connectivity needs to have proper vertices
+ *                          and tree_to_vertex fields.  The tree_to_tree
+ *                          and tree_to_face fields must be allocated
+ *                          and satisfy p4est_connectivity_is_valid (conn)
+ *                          but will be overwritten.  The corner
+ *                          fields will be freed and allocated anew.
+ */
+void                p4est_connectivity_complete (p4est_connectivity_t * conn);
+
+/** Removes corner information of a connectivity
+ *  such that enough information is left to run p4est_connectivity_complete successfully.
+ *  The reduced connectivity still passes p4est_connectivity_is_valid.
+ * \param [in,out] conn     The connectivity to be reduced.
+ */
+void                p4est_connectivity_reduce (p4est_connectivity_t * conn);
+
+/** p4est_connectivity_permute
+ * Given a permutation \a perm of the trees in a connectivity \a conn,
+ * permute the trees of \a conn in place and update \a conn to match.
+ * \param [in,out] conn                The connectivity whose trees are
+ *                                     permuted.
+ * \param [in] perm                    A permutation array, whose elements are
+ *                                     size_t's.
+ * \param [in] is_current_to_new       if true, the jth entry of perm is the
+ *                                     new index for the entry whose current
+ *                                     index is j, otherwise the jth entry of
+ *                                     perm is the current index of the tree
+ *                                     whose index will be j after the
+ *                                     permutation.
+ */
+void                p4est_connectivity_permute (p4est_connectivity_t * conn,
+                                                sc_array_t * perm,
+                                                int is_current_to_new);
+#ifdef P4EST_WITH_METIS
+
+/** p4est_connectivity_reorder
+ * This function takes a connectivity \a conn and a parameter \a k,
+ * which will typically be the number of processes, and reorders the trees
+ * such that if every processes is assigned (num_trees / k) trees, the
+ * communication volume will be minimized.  This is intended for use with
+ * connectivities that contain a large number of trees.  This should be done
+ * BEFORE a p4est is created using the connectivity.  This is done in place:
+ * any data structures that use indices to refer to trees before this
+ * procedure will be invalid.  Note that this routine calls metis and not
+ * parmetis because the connectivity is copied on every process.
+ * A communicator is required because I'm not positive that metis is
+ * deterministic. \a ctype determines when an edge exist between two trees in
+ * the dual graph used by metis in the reordering.
+ * \param [in]     comm       MPI communicator.
+ * \param [in]     k          if k > 0, the number of pieces metis will use to
+ *                            guide the reordering; if k = 0, the number of
+ *                            pieces will be determined from the MPI
+ *                            communicator.
+ * \param [in,out] conn       connectivity that will be reordered.
+ * \param [in]     ctype      determines when an edge exists in the dual graph
+ *                            of the connectivity structure.
+ */
+void                p4est_connectivity_reorder (MPI_Comm comm, int k,
+                                                p4est_connectivity_t * conn,
+                                                p4est_connect_type_t ctype);
+
+#endif /* P4EST_WITH_METIS */
+
+/** p4est_connectivity_join_faces
+ * This function takes an existing valid connectivity \a conn and modifies it
+ * by joining two tree faces that are currently boundary faces.
+ * \param [in,out] conn        connectivity that will be altered.
+ * \param [in]     tree_left   tree that will be on the left side of the joined
+ *                             faces.
+ * \param [in]     tree_right  tree that will be on the right side of the
+ *                             joined faces.
+ * \param [in]     face_left   face of \a tree_left that will be joined.
+ * \param [in]     face_right  face of \a tree_right that will be joined.
+ * \param [in]     orientation the orientation of \a face_left and
+ *                             \a face_right once joined (see the description
+ *                             of p4est_connectivity_t to understand
+ *                             orientation).
+ */
+void                p4est_connectivity_join_faces (p4est_connectivity_t *
+                                                   conn,
+                                                   p4est_topidx_t tree_left,
+                                                   p4est_topidx_t tree_right,
+                                                   int face_left,
+                                                   int face_right,
+                                                   int orientation);
+
+/** p4est_connectivity_is_equivalent
+ * This function compares two connectivities for equivalence: it returns
+ * \a true if they are the same connectivity, or if they have the same
+ * topology.  The definition of topological sameness is strict: there is no
+ * attempt made to determine whether permutation and/or rotation of the trees
+ * makes the connectivities equivalent.
+ *
+ * \param[in]      conn1    a valid connectivity
+ * \param[out]     conn2    a valid connectivity
+ */
+int                 p4est_connectivity_is_equivalent (p4est_connectivity_t *
+                                                      conn1,
+                                                      p4est_connectivity_t *
+                                                      conn2);
+
+/** Return a pointer to a p4est_corner_transform_t array element. */
+/*@unused@*/
+static inline p4est_corner_transform_t *
+p4est_corner_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_corner_transform_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return
+    (p4est_corner_transform_t *) (array->array +
+                                  sizeof (p4est_corner_transform_t) * it);
+}
+
+/** Read an ABAQUS input file from a file stream.
+ *
+ * This utility function reads a basic ABAQUS file supporting element type with
+ * the prefix C2D4, CPS4, and S4 in 2D and of type C3D8 reading them as
+ * bilinear quadrilateral and trilinear hexahedral trees respectively.
+ *
+ * A basic 2D mesh is given below.  The \c *Node section gives the vertex
+ * number and x, y, and z components for each vertex.  The \c *Element section
+ * gives the 4 vertices in 2D (8 vertices in 3D) of each element in counter
+ * clockwise order. So in 2D the nodes are given as:
+ *
+ *   4                     3
+ *   +-------------------+
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   +-------------------+
+ *   1                   2
+ *
+ * and in 3D they are given as:
+ *
+ * 8                     7
+ *  +---------------------+
+ *  |\                    |\
+ *  | \                   | \
+ *  |  \                  |  \
+ *  |   \                 |   \
+ *  |   5+---------------------+6
+ *  |    |                |    |
+ *  +----|----------------+    |
+ *  4\   |               3 \   |
+ *    \  |                  \  |
+ *     \ |                   \ |
+ *      \|                    \|
+ *       +---------------------+
+ *       1                     2
+ *
+ * \code
+ * *Heading
+ *  box.inp
+ * *Node
+ * 1,  -5, -5, 0
+ * 2,   5, -5, 0
+ * 3,   5,  5, 0
+ * 4,  -5,  5, 0
+ * 5,   0, -5, 0
+ * 6,   5,  0, 0
+ * 7,   0,  5, 0
+ * 8,  -5,  0, 0
+ * 9,   1, -1, 0
+ * 10,  0,  0, 0
+ * 11, -2,  1, 0
+ * *Element, type=CPS4, ELSET=Surface1
+ * 1,  1, 10, 11, 8
+ * 2,  3, 10, 9,  6
+ * 3,  9, 10, 1,  5
+ * 4,  7,  4, 8, 11
+ * 5, 11, 10, 3,  7
+ * 6,  2,  6, 9,  5
+ * \endcode
+ *
+ * This code can be called two ways.  The first, when \c vertex==NULL and \c
+ * tree_to_vertex==NULL, is used to count the number of tress and vertices in
+ * the connectivity to be generated by the \c .inp mesh in the \a stream.  The
+ * second, when \c vertices!=NULL and \c tree_to_vertex!=NULL, fill \c vertices
+ * and \c tree_to_vertex.  In this case \c num_vertices and \c num_trees need
+ * to be set to the maximum number of entries allocated in \c vertices and \c
+ * tree_to_vertex.
+ *
+ * \param[in,out]  stream         file stream to read the connectivity from
+ * \param[in,out]  num_vertices   the number of vertices in the connectivity
+ * \param[in,out]  num_trees      the number of trees in the connectivity
+ * \param[out]     vertices       the list of \c vertices of the connectivity
+ * \param[out]     tree_to_vertex the \c tree_to_vertex map of the connectivity
+ *
+ * \returns 0 if successful and nonzero if not
+ */
+int                 p4est_connectivity_read_inp_stream (FILE * stream,
+                                                        p4est_topidx_t *
+                                                        num_vertices,
+                                                        p4est_topidx_t *
+                                                        num_trees,
+                                                        double *vertices,
+                                                        p4est_topidx_t *
+                                                        tree_to_vertex);
+
+/** Create a p4est connectivity from an ABAQUS input file.
+ *
+ * This utility function reads a basic ABAQUS file supporting element type with
+ * the prefix C2D4, CPS4, and S4 in 2D and of type C3D8 reading them as
+ * bilinear quadrilateral and trilinear hexahedral trees respectively.
+ *
+ * A basic 2D mesh is given below.  The \c *Node section gives the vertex
+ * number and x, y, and z components for each vertex.  The \c *Element section
+ * gives the 4 vertices in 2D (8 vertices in 3D) of each element in counter
+ * clockwise order. So in 2D the nodes are given as:
+ *
+ *   4                     3
+ *   +-------------------+
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   +-------------------+
+ *   1                   2
+ *
+ * and in 3D they are given as:
+ *
+ * 8                     7
+ *  +---------------------+
+ *  |\                    |\
+ *  | \                   | \
+ *  |  \                  |  \
+ *  |   \                 |   \
+ *  |   5+---------------------+6
+ *  |    |                |    |
+ *  +----|----------------+    |
+ *  4\   |               3 \   |
+ *    \  |                  \  |
+ *     \ |                   \ |
+ *      \|                    \|
+ *       +---------------------+
+ *       1                     2
+ *
+ * \code
+ * *Heading
+ *  box.inp
+ * *Node
+ * 1,  -5, -5, 0
+ * 2,   5, -5, 0
+ * 3,   5,  5, 0
+ * 4,  -5,  5, 0
+ * 5,   0, -5, 0
+ * 6,   5,  0, 0
+ * 7,   0,  5, 0
+ * 8,  -5,  0, 0
+ * 9,   1, -1, 0
+ * 10,  0,  0, 0
+ * 11, -2,  1, 0
+ * *Element, type=CPS4, ELSET=Surface1
+ * 1,  1, 10, 11, 8
+ * 2,  3, 10, 9,  6
+ * 3,  9, 10, 1,  5
+ * 4,  7,  4, 8, 11
+ * 5, 11, 10, 3,  7
+ * 6,  2,  6, 9,  5
+ * \endcode
+ *
+ * This function reads a mesh from \a filename and returns an associated p4est
+ * connectivity.
+ *
+ * \param[in]  filename         file to read the connectivity from
+ *
+ * \return  an allocated connectivity associated with the mesh in \a filename
+ *          or NULL if an error occurred.
+ */
+p4est_connectivity_t *p4est_connectivity_read_inp (const char *filename);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_CONNECTIVITY_H */
diff --git a/src/p4est_extended.h b/src/p4est_extended.h
new file mode 100644
index 0000000..4732c32
--- /dev/null
+++ b/src/p4est_extended.h
@@ -0,0 +1,308 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/********************************************************************
+ *                          IMPORTANT NOTE                          *
+ *                                                                  *
+ * These interfaces are intended for those who like finer control.  *
+ * The API offers extended versions of some basic p4est functions.  *
+ * The API may change without notice.                               *
+ ********************************************************************/
+
+/** \file p4est_extended.h
+ *
+ * Interface routines with extended capabilities.
+ *
+ * \ingroup p4est
+ */
+
+#ifndef P4EST_EXTENDED_H
+#define P4EST_EXTENDED_H
+
+#include <p4est.h>
+#include <p4est_mesh.h>
+#include <p4est_iterate.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Data pertaining to selecting, inspecting, and profiling algorithms.
+ * A pointer to this structure is hooked into the p4est main structure.
+ *
+ *
+ * The balance_ranges and balance_notify* times are collected
+ * whenever an inspect structure is present in p4est.
+ */
+/* TODO: Describe the purpose of various switches, counters, and timings. */
+struct p4est_inspect
+{
+  /** Use sc_ranges to determine the asymmetric communication pattern.
+   * If \a use_balance_ranges is false (the default), sc_notify is used. */
+  int                 use_balance_ranges;
+  /** If true, call both sc_ranges and sc_notify and verify consistency.
+   * Which is actually used is still determined by \a use_balance_ranges. */
+  int                 use_balance_ranges_notify;
+  /** Verify sc_ranges and/or sc_notify as applicable. */
+  int                 use_balance_verify;
+  /** If positive and smaller than p4est_num ranges, overrides it */
+  int                 balance_max_ranges;
+  size_t              balance_A_count_in;
+  size_t              balance_A_count_out;
+  size_t              balance_comm_sent;
+  size_t              balance_comm_nzpeers;
+  size_t              balance_B_count_in;
+  size_t              balance_B_count_out;
+  size_t              balance_zero_sends[2], balance_zero_receives[2];
+  double              balance_A;
+  double              balance_comm;
+  double              balance_B;
+  double              balance_ranges;   /**< time spent in sc_ranges */
+  double              balance_notify;   /**< time spent in sc_notify */
+  /** time spent in sc_notify_allgather */
+  double              balance_notify_allgather;
+  int                 use_B;
+};
+
+/** Callback function prototype to replace one set of quadrants with another.
+ *
+ * This is used by extended routines when the quadrants of an existing, valid
+ * p4est are changed.  The callback allows the user to make changes to newly
+ * initialized quadrants before the quadrants that they replace are destroyed.
+ *
+ * \param [in] num_outgoing The number of outgoing quadrants.
+ * \param [in] outgoing     The outgoing quadrants: after the callback, the
+ *                          user_data, if \a p4est->data_size is nonzero,
+ *                          will be destroyed.
+ * \param [in] num_incoming The number of incoming quadrants.
+ * \param [in,out] incoming The incoming quadrants: prior to the callback,
+ *                          the user_data, if \a p4est->data_size is nonzero,
+ *                          is allocated, and the p4est_init_t callback,
+ *                          if it has been provided, will be called.
+ *
+ * If the mesh is being refined, num_outgoing will be 1 and num_incoming will
+ * be 4, and vice versa if the mesh is being coarsened.
+ */
+typedef void        (*p4est_replace_t) (p4est_t * p4est,
+                                        p4est_topidx_t which_tree,
+                                        int num_outgoing,
+                                        p4est_quadrant_t * outgoing[],
+                                        int num_incoming,
+                                        p4est_quadrant_t * incoming[]);
+
+/** Create a new forest.
+ * This is a more general form of p4est_new.
+ * See the documentation of p4est_new for basic usage.
+ *
+ * \param [in] min_quadrants    Minimum initial quadrants per processor.
+ *                              Makes the refinement pattern mpisize-specific.
+ * \param [in] min_level        The forest is refined at least to this level.
+ *                              May be negative or 0, then it has no effect.
+ * \param [in] fill_uniform     If true, fill the forest with a uniform mesh
+ *                              instead of the coarsest possible one.
+ *                              The latter is partition-specific so that
+ *                              is usually not a good idea.
+ */
+p4est_t            *p4est_new_ext (sc_MPI_Comm mpicomm,
+                                   p4est_connectivity_t * connectivity,
+                                   p4est_locidx_t min_quadrants,
+                                   int min_level, int fill_uniform,
+                                   size_t data_size, p4est_init_t init_fn,
+                                   void *user_pointer);
+
+/** Create a new mesh.
+ * \param [in] p4est                A forest that is fully 2:1 balanced.
+ * \param [in] ghost                The ghost layer created from the
+ *                                  provided p4est.
+ * \param [in] compute_tree_index   Boolean to decide whether to allocate and
+ *                                  compute the quad_to_tree list.
+ * \param [in] compute_level_lists  Boolean to decide whether to compute the
+ *                                  level lists in quad_level.
+ * \param [in] btype                Currently ignored, only face neighbors
+ *                                  are stored.
+ * \return                          A fully allocated mesh structure.
+ */
+p4est_mesh_t       *p4est_mesh_new_ext (p4est_t * p4est,
+                                        p4est_ghost_t * ghost,
+                                        int compute_tree_index,
+                                        int compute_level_lists,
+                                        p4est_connect_type_t btype);
+
+/** Refine a forest with a bounded refinement level and a replace option.
+ * \param [in,out] p4est The forest is changed in place.
+ * \param [in] refine_recursive Boolean to decide on recursive refinement.
+ * \param [in] maxlevel   Maximum allowed refinement level (inclusive).
+ *                        If this is negative the level is restricted only
+ *                        by the compile-time constant QMAXLEVEL in p4est.h.
+ * \param [in] refine_fn  Callback function that must return true if a quadrant
+ *                        shall be refined.  If refine_recursive is true,
+ *                        refine_fn is called for every existing and newly
+ *                        created quadrant.  Otherwise, it is called for every
+ *                        existing quadrant.  It is possible that a refinement
+ *                        request made by the callback is ignored.  To catch
+ *                        this case, you can examine whether init_fn or
+ *                        replace_fn gets called.
+ * \param [in] init_fn    Callback function to initialize the user_data for
+ *                        newly created quadrants, which is guaranteed to be
+ *                        allocated.  This function pointer may be NULL.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace; may be NULL.
+ */
+void                p4est_refine_ext (p4est_t * p4est,
+                                      int refine_recursive, int maxlevel,
+                                      p4est_refine_t refine_fn,
+                                      p4est_init_t init_fn,
+                                      p4est_replace_t replace_fn);
+
+/** Coarsen a forest.
+ * \param [in,out] p4est The forest is changed in place.
+ * \param [in] coarsen_recursive Boolean to decide on recursive coarsening.
+ * \param [in] callback_orphans Boolean to enable calling coarsen_fn even on
+ *                        non-families.  In this case, the second quadrant
+ *                        pointer in the argument list of the callback is NULL,
+ *                        subsequent pointers are undefined, and the return
+ *                        value is ignored.  If coarsen_recursive is true, it
+ *                        is possible that a quadrant is called once or more as
+ *                        an orphan and eventually becomes part of a family.
+ * \param [in] coarsen_fn Callback function that returns true if a
+ *                        family of quadrants shall be coarsened.
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace.
+ */
+void                p4est_coarsen_ext (p4est_t * p4est, int coarsen_recursive,
+                                       int callback_orphans,
+                                       p4est_coarsen_t coarsen_fn,
+                                       p4est_init_t init_fn,
+                                       p4est_replace_t replace_fn);
+
+/** 2:1 balance the size differences of neighboring elements in a forest.
+ * \param [in,out] p4est  The p4est to be worked on.
+ * \param [in] btype      Balance type (face or corner/full).
+ *                        Corner balance is almost never required when
+ *                        discretizing a PDE; just causes smoother mesh grading.
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace.
+ */
+void                p4est_balance_ext (p4est_t * p4est,
+                                       p4est_connect_type_t btype,
+                                       p4est_init_t init_fn,
+                                       p4est_replace_t replace_fn);
+
+void                p4est_balance_subtree_ext (p4est_t * p4est,
+                                               p4est_connect_type_t btype,
+                                               p4est_topidx_t which_tree,
+                                               p4est_init_t init_fn,
+                                               p4est_replace_t replace_fn);
+
+/** Repartition the forest.
+ *
+ * The forest is partitioned between processors such that each processor
+ * has an approximately equal number of quadrants (or weight).
+ *
+ * \param [in,out] p4est      The forest that will be partitioned.
+ * \param [in]     partition_for_coarsening     If true, the partition
+ *                            is modified to allow one level of coarsening.
+ * \param [in]     weight_fn  A weighting function or NULL
+ *                            for uniform partitioning.
+ * \return         The global number of shipped quadrants
+ */
+p4est_gloidx_t      p4est_partition_ext (p4est_t * p4est,
+                                         int partition_for_coarsening,
+                                         p4est_weight_t weight_fn);
+
+/** p4est_iterate_ext adds the option \a remote: if this is false, then it is
+ * the same as p4est_iterate; if this is true, then corner callbacks are also
+ * called on corners for hanging faces touched by local quadrants.
+ */
+void                p4est_iterate_ext (p4est_t * p4est,
+                                       p4est_ghost_t * ghost_layer,
+                                       void *user_data,
+                                       p4est_iter_volume_t iter_volume,
+                                       p4est_iter_face_t iter_face,
+                                       p4est_iter_corner_t iter_corner,
+                                       int remote);
+
+/** Save the complete connectivity/p4est data to disk.  This is a collective
+ * operation that all MPI processes need to call.  All processes write
+ * into the same file, so the filename given needs to be identical over
+ * all parallel invocations.
+ * See p4est_load_ext for information on the autopartition parameter.
+ * \param [in] filename    Name of the file to write.
+ * \param [in] p4est       Valid forest structure.
+ * \param [in] save_data   If true, the element data is saved.
+ *                         Otherwise, a data size of 0 is saved.
+ * \param [in] save_partition   If false, save file as if 1 core was used.
+ *                              If true, save core count and partition.
+ *                         Advantage: Partition can be recovered on loading
+ *                              with same mpisize and autopartition false.
+ *                         Disadvantage: Makes the file depend on mpisize.
+ *                  Either way the file can be loaded with autopartition true.
+ * \note            Aborts on file errors.
+ */
+void                p4est_save_ext (const char *filename, p4est_t * p4est,
+                                    int save_data, int save_partition);
+
+/** Load the complete connectivity/p4est structure from disk.
+ * It is possible to load the file with a different number of processors
+ * than has been used to write it.  The partition will then be uniform.
+ * \param [in] filename         Name of the file to read.
+ * \param [in] mpicomm          A valid MPI communicator.
+ * \param [in] data_size        Size of data for each quadrant which can be
+ *                              zero.  Then user_data_pool is set to NULL.
+ *                              If data_size is zero, load_data is ignored.
+ * \param [in] load_data        If true, the element data is loaded.  This is
+ *                              only permitted if the saved data size matches.
+ *                              If false, the stored data size is ignored.
+ * \param [in] autopartition    Ignore saved partition and make it uniform.
+ * \param [in] broadcasthead    Have only rank 0 read headers and bcast them.
+ * \param [in] user_pointer     Assign to the user_pointer member of the p4est
+ *                              before init_fn is called the first time.
+ * \param [out] connectivity    Connectivity must be destroyed separately.
+ * \return          Returns a valid forest structure. A pointer to a valid
+ *                  connectivity structure is returned through the last
+ *                  argument.
+ * \note            Aborts on file errors or invalid file contents.
+ */
+p4est_t            *p4est_load_ext (const char *filename, sc_MPI_Comm mpicomm,
+                                    size_t data_size, int load_data,
+                                    int autopartition, int broadcasthead,
+                                    void *user_pointer,
+                                    p4est_connectivity_t ** connectivity);
+
+/** The same as p4est_load_ext, but reading the connectivity/p4est from an
+ * open sc_io_source_t stream.
+ */
+p4est_t            *p4est_source_ext (sc_io_source_t * src,
+                                      sc_MPI_Comm mpicomm, size_t data_size,
+                                      int load_data, int autopartition,
+                                      int broadcasthead, void *user_pointer,
+                                      p4est_connectivity_t ** connectivity);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_EXTENDED_H */
diff --git a/src/p4est_geometry.c b/src/p4est_geometry.c
new file mode 100644
index 0000000..3b38482
--- /dev/null
+++ b/src/p4est_geometry.c
@@ -0,0 +1,102 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * \file p4est_geometry.c
+ * We provide the identity transformation for reference.
+ * Please implement p4est_geometry_t as you see fit.
+ */
+
+#ifndef P4_TO_P8
+#include <p4est_geometry.h>
+#else
+#include <p8est_geometry.h>
+#endif
+
+void
+p4est_geometry_destroy (p4est_geometry_t * geom)
+{
+  if (geom->destroy != NULL) {
+    geom->destroy (geom);
+  }
+  else {
+    P4EST_FREE (geom);
+  }
+}
+
+static void
+p4est_geometry_connectivity_X (p4est_geometry_t * geom,
+                               p4est_topidx_t which_tree,
+                               const double abc[3], double xyz[3])
+{
+  p4est_connectivity_t *connectivity = (p4est_connectivity_t *) geom->user;
+  const p4est_topidx_t *tree_to_vertex = connectivity->tree_to_vertex;
+  const double       *v = connectivity->vertices;
+  double              eta_x, eta_y, eta_z = 0.;
+  int                 j, k;
+  p4est_topidx_t      vt[P4EST_CHILDREN];
+
+  /* retrieve corners of the tree */
+  for (k = 0; k < P4EST_CHILDREN; ++k) {
+    vt[k] = tree_to_vertex[which_tree * P4EST_CHILDREN + k];
+  }
+
+  /* these are reference coordinates in [0, 1]**d */
+  eta_x = abc[0];
+  eta_y = abc[1];
+  eta_z = abc[2];
+
+  /* bi/trilinear transformation */
+  for (j = 0; j < 3; ++j) {
+    /* *INDENT-OFF* */
+    xyz[j] =
+           ((1. - eta_z) * ((1. - eta_y) * ((1. - eta_x) * v[3 * vt[0] + j] +
+                                                  eta_x  * v[3 * vt[1] + j]) +
+                                  eta_y  * ((1. - eta_x) * v[3 * vt[2] + j] +
+                                                  eta_x  * v[3 * vt[3] + j]))
+#ifdef P4_TO_P8
+            +     eta_z  * ((1. - eta_y) * ((1. - eta_x) * v[3 * vt[4] + j] +
+                                                  eta_x  * v[3 * vt[5] + j]) +
+                                  eta_y  * ((1. - eta_x) * v[3 * vt[6] + j] +
+                                                  eta_x  * v[3 * vt[7] + j]))
+#endif
+           );
+    /* *INDENT-ON* */
+  }
+}
+
+p4est_geometry_t   *
+p4est_geometry_new_connectivity (p4est_connectivity_t * conn)
+{
+  p4est_geometry_t   *geom;
+
+  P4EST_ASSERT (conn->vertices != NULL);
+
+  geom = P4EST_ALLOC_ZERO (p4est_geometry_t, 1);
+
+  geom->name = P4EST_STRING "_connectivity";
+  geom->user = conn;
+  geom->X = p4est_geometry_connectivity_X;
+
+  return geom;
+}
diff --git a/src/p4est_geometry.h b/src/p4est_geometry.h
new file mode 100644
index 0000000..51ac1bd
--- /dev/null
+++ b/src/p4est_geometry.h
@@ -0,0 +1,84 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_geometry.h transforms from vertex frame to physical space
+ *
+ * \ingroup p4est
+ */
+
+#ifndef P4EST_GEOMETRY_H
+#define P4EST_GEOMETRY_H
+
+#include <p4est_connectivity.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef struct p4est_geometry p4est_geometry_t;
+
+/** Forward transformation from the reference unit square to physical space.
+ * Note that the two-dimensional connectivities have 3D vertex coordinates
+ * that can be used in the transformation if so desired.
+ * The physical space "xyz" is user-defined, currently used for VTK output.
+ */
+typedef void        (*p4est_geometry_X_t) (p4est_geometry_t * geom,
+                                           p4est_topidx_t which_tree,
+                                           const double abc[3],
+                                           double xyz[3]);
+
+/** Destructor prototype for a user-allocated \a p4est_geometry_t.
+ * It is invoked by p4est_geometry_destroy.  If the user chooses to
+ * reserve the structure statically, simply don't call p4est_geometry_destroy.
+ */
+typedef void        (*p4est_geometry_destroy_t) (p4est_geometry_t * geom);
+
+/** This structure can be filled or allocated by the user.
+ * p4est will never change its contents.
+ */
+struct p4est_geometry
+{
+  const char         *name;     /**< User's choice is arbitrary. */
+  void               *user;     /**< User's choice is arbitrary. */
+  p4est_geometry_X_t  X;        /**< Coordinate transformation. */
+  p4est_geometry_destroy_t destroy;     /**< Destructor called by
+                                             p4est_geometry_destroy.  If
+                                             NULL, P4EST_FREE is called. */
+};
+
+/** Can be used to conveniently destroy a geometry structure.
+ * The user is free not to call this function at all if they handle the
+ * memory of the p4est_geometry_t in their own way.
+ */
+void                p4est_geometry_destroy (p4est_geometry_t * geom);
+
+/** Create a geometry structure based on the vertices in a connectivity.
+ * The transformation is constructed using bilinear interpolation.
+ * \param [in] conn A p4est_connectivity_t with valid vertices.  We do NOT
+ *                  take ownership and expect this structure to stay alive.
+ * \return          Geometry structure; use with p4est_geometry_destroy.
+ */
+p4est_geometry_t   *p4est_geometry_new_connectivity (p4est_connectivity_t *
+                                                     conn);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_GEOMETRY_H */
diff --git a/src/p4est_ghost.c b/src/p4est_ghost.c
new file mode 100644
index 0000000..6d948e5
--- /dev/null
+++ b/src/p4est_ghost.c
@@ -0,0 +1,3931 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_communication.h>
+#include <p4est_ghost.h>
+#include <p4est_search.h>
+#include <p4est_lnodes.h>
+#include <p4est_algorithms.h>
+#else
+/* bits and communication are included in p8est_ghost.c */
+#include <p8est_ghost.h>
+#include <p8est_search.h>
+#include <p8est_lnodes.h>
+#include <p8est_algorithms.h>
+#endif
+#include <sc_search.h>
+
+/* htonl is in either of these two */
+#ifdef P4EST_HAVE_ARPA_NET_H
+#include <arpa/inet.h>
+#endif
+#ifdef P4EST_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+typedef enum
+{
+  P4EST_GHOST_UNBALANCED_ABORT = 0,
+  P4EST_GHOST_UNBALANCED_FAIL,
+  P4EST_GHOST_UNBALANCED_ALLOW
+}
+p4est_ghost_tolerance_t;
+
+size_t
+p4est_ghost_memory_used (p4est_ghost_t * ghost)
+{
+  return sizeof (p4est_ghost_t) +
+    sc_array_memory_used (&ghost->ghosts, 0) +
+    (ghost->mpisize + 1) * sizeof (p4est_locidx_t) +
+    (ghost->num_trees + 1) * sizeof (p4est_locidx_t);
+}
+
+#ifdef P4EST_ENABLE_MPI
+
+static inline sc_array_t *
+p4est_ghost_array_index (sc_array_t * array, int i)
+{
+  return (sc_array_t *) sc_array_index_int (array, i);
+}
+
+#endif
+
+static p4est_ghost_t *p4est_ghost_new_check (p4est_t * p4est,
+                                             p4est_connect_type_t btype,
+                                             p4est_ghost_tolerance_t tol);
+
+int
+p4est_quadrant_find_owner (p4est_t * p4est, p4est_topidx_t treeid,
+                           int face, const p4est_quadrant_t * q)
+{
+  const int           rank = p4est->mpirank;
+  p4est_connectivity_t *conn = p4est->connectivity;
+#ifdef P4EST_ENABLE_DEBUG
+  int                 dims;
+#endif
+  int                 ftransform[P4EST_FTRANSFORM];
+  p4est_topidx_t      ntreeid, ntreeid2;
+  p4est_quadrant_t    nq;
+
+  if (p4est_quadrant_is_inside_root (q)) {
+    return p4est_comm_find_owner (p4est, treeid, q, rank);
+  }
+
+  P4EST_QUADRANT_INIT (&nq);
+
+  /* We are outside of the unit tree */
+  if (face != -1) {
+    P4EST_ASSERT (face >= 0 && face < P4EST_FACES);
+    P4EST_ASSERT (treeid >= 0 && treeid < conn->num_trees);
+    ntreeid = conn->tree_to_tree[P4EST_FACES * treeid + face];
+    if (ntreeid == treeid
+        && ((int) conn->tree_to_face[P4EST_FACES * treeid + face] == face)) {
+      /* This quadrant goes across a face with no neighbor */
+      return -1;
+    }
+  }
+  else {
+    /* We need to determine the face ourselves */
+    const p4est_qcoord_t rh = P4EST_ROOT_LEN;
+    int                 quad_contact[P4EST_FACES];
+
+    quad_contact[0] = (q->x < 0);
+    quad_contact[1] = (q->x >= rh);
+    quad_contact[2] = (q->y < 0);
+    quad_contact[3] = (q->y >= rh);
+#ifdef P4_TO_P8
+    quad_contact[4] = (q->z < 0);
+    quad_contact[5] = (q->z >= rh);
+#endif
+
+    /* Make sure we are neither tree edge nor tree corner */
+#ifdef P4EST_ENABLE_DEBUG
+    dims = (((quad_contact[0] || quad_contact[1]) ? 1 : 0) +
+            ((quad_contact[2] || quad_contact[3]) ? 1 : 0));
+#ifdef P4_TO_P8
+    dims += ((quad_contact[4] || quad_contact[5]) ? 1 : 0);
+#endif
+    P4EST_ASSERT (dims == 1);
+#endif
+
+    ntreeid = -1;
+    for (face = 0; face < P4EST_FACES; ++face) {
+      if (quad_contact[face]) {
+        ntreeid = conn->tree_to_tree[P4EST_FACES * treeid + face];
+        if (ntreeid == treeid
+            && ((int) conn->tree_to_face[P4EST_FACES * treeid + face] ==
+                face)) {
+          /* This quadrant goes across a face with no neighbor */
+          return -1;
+        }
+        break;
+      }
+    }
+    P4EST_ASSERT (face < P4EST_FACES && ntreeid >= 0);
+  }
+
+  ntreeid2 = p4est_find_face_transform (conn, treeid, face, ftransform);
+  P4EST_ASSERT (ntreeid2 == ntreeid);
+  p4est_quadrant_transform_face (q, &nq, ftransform);
+
+  return p4est_comm_find_owner (p4est, ntreeid, &nq, rank);
+}
+
+#ifdef P4EST_ENABLE_MPI
+
+/** Gets the procids of the owners of \a q.
+ *
+ * For a quadrant across the corner of a tree has possibly multiple
+ * trees in which it lives, and thus multiple procs.
+ *
+ * \param [in]     p4est      The forest in which to search for \a q.
+ * \param [in]     treeid     The tree id to which \a q belongs.
+ * \param [in]     treecorner The r-corner of the tree \a q is across from.
+ * \param [in]     q          The quadrant that is being searched for.
+ * \param [in,out] qprocs     Starts as an initialized array and ends with
+ *                            the list of processors that \a q belongs too.
+ * \param [out]    nurgood    If not NULL, check if the smallest quadrant
+ *                            in the upper right corner of q after transform
+ *                            has the same owner.
+ */
+static void
+p4est_quadrant_find_tree_corner_owners (p4est_t * p4est,
+                                        p4est_topidx_t treeid,
+                                        int treecorner,
+                                        const p4est_quadrant_t * q,
+                                        sc_array_t * q_procs, int *nurgood)
+{
+  const int           rank = p4est->mpirank;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  int                *proc, nurproc;
+  size_t              ctree;
+  p4est_quadrant_t    cq;
+  p4est_corner_info_t ci;
+  p4est_corner_transform_t *ct;
+  sc_array_t         *cta;
+
+  P4EST_ASSERT (p4est_quadrant_is_outside_corner (q));
+
+  P4EST_QUADRANT_INIT (&cq);
+
+  /* Find all corners that are not myself or from a face neighbor */
+  cta = &ci.corner_transforms;
+  sc_array_init (cta, sizeof (p4est_corner_transform_t));
+  p4est_find_corner_transform (conn, treeid, treecorner, &ci);
+
+  sc_array_resize (q_procs, 0);
+  if (nurgood != NULL) {
+    *nurgood = 1;
+    if (q->level == P4EST_QMAXLEVEL)
+      nurgood = NULL;
+  }
+
+  for (ctree = 0; ctree < cta->elem_count; ++ctree) {
+    ct = p4est_corner_array_index (cta, ctree);
+
+    cq = *q;
+    p4est_quadrant_transform_corner (&cq, (int) ct->ncorner, 1);
+
+    proc = (int *) sc_array_push (q_procs);
+    *proc = p4est_comm_find_owner (p4est, ct->ntree, &cq, rank);
+
+    if (nurgood != NULL) {
+      p4est_quadrant_last_descendant (&cq, &cq, P4EST_QMAXLEVEL);
+      nurproc = p4est_comm_find_owner (p4est, ct->ntree, &cq, *proc);
+      *nurgood = *nurgood && (nurproc == *proc);
+    }
+  }
+
+  sc_array_reset (cta);
+}
+
+#endif /* P4EST_ENABLE_MPI */
+
+/* Returns true for matching proc and tree range, false otherwise. */
+static int
+p4est_ghost_check_range (p4est_ghost_t * ghost,
+                         int which_proc, p4est_topidx_t which_tree,
+                         size_t * pstart, size_t * pended)
+{
+  size_t              start = 0;
+  size_t              ended = ghost->ghosts.elem_count;
+
+  if (ghost->ghosts.elem_count == 0) {
+    *pstart = *pended = 0;
+    return 0;
+  }
+
+  if (which_proc != -1) {
+    P4EST_ASSERT (0 <= which_proc && which_proc < ghost->mpisize);
+    start = SC_MAX (start, (size_t) ghost->proc_offsets[which_proc]);
+    ended = SC_MIN (ended, (size_t) ghost->proc_offsets[which_proc + 1]);
+  }
+  if (which_tree != -1) {
+    P4EST_ASSERT (0 <= which_tree && which_tree < ghost->num_trees);
+    start = SC_MAX (start, (size_t) ghost->tree_offsets[which_tree]);
+    ended = SC_MIN (ended, (size_t) ghost->tree_offsets[which_tree + 1]);
+  }
+
+  *pstart = start;
+  *pended = ended;
+  return start < ended;
+}
+
+ssize_t
+p4est_ghost_bsearch (p4est_ghost_t * ghost,
+                     int which_proc, p4est_topidx_t which_tree,
+                     const p4est_quadrant_t * q)
+{
+  size_t              start, ended;
+
+  if (p4est_ghost_check_range (ghost, which_proc, which_tree, &start, &ended)) {
+    ssize_t             result;
+    sc_array_t          ghost_view;
+
+    /* create a per-tree window on the ghost layer */
+    sc_array_init_view (&ghost_view, &ghost->ghosts, start, ended - start);
+    result = sc_array_bsearch (&ghost_view, q, p4est_quadrant_compare);
+
+    /* and don't forget to add the window offset */
+    return (result < 0) ? (ssize_t) (-1) : result + (ssize_t) start;
+  }
+  else {
+    P4EST_ASSERT (p4est_quadrant_is_valid (q));
+    return -1;
+  }
+}
+
+ssize_t
+p4est_ghost_contains (p4est_ghost_t * ghost,
+                      int which_proc, p4est_topidx_t which_tree,
+                      const p4est_quadrant_t * q)
+{
+  size_t              start, ended;
+
+  if (p4est_ghost_check_range (ghost, which_proc, which_tree, &start, &ended)) {
+    size_t              nmemb = ended - start - 1;
+    size_t              result;
+    sc_array_t          ghost_view;
+    p4est_quadrant_t   *qresult;
+
+    /* create a per-tree window on the ghost layer */
+    sc_array_init_view (&ghost_view, &ghost->ghosts, start, ended - start);
+    result = sc_bsearch_range (q, ghost_view.array,
+                               nmemb, sizeof (p4est_quadrant_t),
+                               p4est_quadrant_compare);
+    qresult = p4est_quadrant_array_index (&ghost_view, result);
+
+    /* and don't forget to add the window offset */
+    return !(p4est_quadrant_is_equal (qresult, q) ||
+             p4est_quadrant_is_ancestor (qresult, q)) ?
+      (ssize_t) (-1) : result + (ssize_t) start;
+  }
+  else {
+    P4EST_ASSERT (p4est_quadrant_is_valid (q));
+    return -1;
+  }
+}
+
+int
+p4est_quadrant_exists (p4est_t * p4est, p4est_ghost_t * ghost,
+                       p4est_topidx_t treeid, const p4est_quadrant_t * q,
+                       sc_array_t * exists_arr,
+                       sc_array_t * rproc_arr, sc_array_t * rquad_arr)
+{
+  const int           rank = p4est->mpirank;
+  const p4est_qcoord_t rh = P4EST_ROOT_LEN;
+  int                 qproc, face, edge, corner;
+  int                 ftransform[P4EST_FTRANSFORM];
+  int                *pexists;
+  int                 face_axis[3];     /* 3 not P4EST_DIM */
+  int                 quad_contact[P4EST_FACES];
+  int                 quad_corner;
+  int                 exists;
+  size_t              ctreeidz;
+  ssize_t             lnid;
+  p4est_topidx_t      tqtreeid;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  p4est_tree_t       *tree = p4est_tree_array_index (p4est->trees, treeid);
+  p4est_tree_t       *tqtree;
+  p4est_quadrant_t    tq, non_existent, *rquad;
+  sc_array_t         *quadrants = &tree->quadrants;
+  sc_array_t         *ta;
+#ifdef P4_TO_P8
+  p8est_edge_info_t   ei;
+  p8est_edge_transform_t *et;
+#endif
+  p4est_corner_info_t ci;
+  p4est_corner_transform_t *ct;
+
+  if (exists_arr != NULL) {
+    P4EST_ASSERT (exists_arr->elem_size == sizeof (int));
+    sc_array_resize (exists_arr, 0);
+  }
+  if (rproc_arr != NULL) {
+    P4EST_ASSERT (rproc_arr->elem_size == sizeof (int));
+    sc_array_resize (rproc_arr, 0);
+  }
+  if (rquad_arr != NULL) {
+    P4EST_ASSERT (rquad_arr->elem_size == sizeof (p4est_quadrant_t));
+    sc_array_resize (rquad_arr, 0);
+  }
+  P4EST_QUADRANT_INIT (&non_existent);
+  ta = NULL;
+
+  if (non_existent.level == q->level) {
+    return 0;
+  }
+
+  /* q is in the unit domain */
+  if (p4est_quadrant_is_inside_root (q)) {
+    qproc = p4est_comm_find_owner (p4est, treeid, q, rank);
+    if (qproc == rank) {
+      lnid = sc_array_bsearch (quadrants, q, p4est_quadrant_compare);
+    }
+    else {
+      lnid = p4est_ghost_bsearch (ghost, qproc, treeid, q);
+      P4EST_ASSERT (lnid == -1 ||
+                    (ghost->proc_offsets[qproc] <= lnid &&
+                     lnid < ghost->proc_offsets[qproc + 1]));
+    }
+    if (rproc_arr != NULL) {
+      *(int *) sc_array_push (rproc_arr) = qproc;
+    }
+    if (rquad_arr != NULL) {
+      rquad = (p4est_quadrant_t *) sc_array_push (rquad_arr);
+      *rquad = *q;
+      rquad->p.piggy3.which_tree = treeid;
+      rquad->p.piggy3.local_num = (p4est_locidx_t) lnid;
+    }
+    return (lnid != -1);
+  }
+
+  /* q is in a neighboring tree */
+  quad_contact[0] = (q->x < 0);
+  quad_contact[1] = (q->x >= rh);
+  face_axis[0] = quad_contact[0] || quad_contact[1];
+  quad_contact[2] = (q->y < 0);
+  quad_contact[3] = (q->y >= rh);
+  face_axis[1] = quad_contact[2] || quad_contact[3];
+#ifndef P4_TO_P8
+  face_axis[2] = 0;
+#else
+  quad_contact[4] = (q->z < 0);
+  quad_contact[5] = (q->z >= rh);
+  face_axis[2] = quad_contact[4] || quad_contact[5];
+#endif
+  quad_corner = 0;
+  face = edge = corner = -1;
+  P4EST_ASSERT (face_axis[0] || face_axis[1] || face_axis[2]);
+  if (!face_axis[1] && !face_axis[2]) {
+    face = 0 + quad_contact[1];
+  }
+  else if (!face_axis[0] && !face_axis[2]) {
+    face = 2 + quad_contact[3];
+  }
+#ifdef P4_TO_P8
+  else if (!face_axis[0] && !face_axis[1]) {
+    face = 4 + quad_contact[5];
+  }
+  else if (!face_axis[0]) {
+    edge = 0 + 2 * quad_contact[5] + quad_contact[3];
+    quad_corner = 1;
+  }
+  else if (!face_axis[1]) {
+    edge = 4 + 2 * quad_contact[5] + quad_contact[1];
+    quad_corner = 1;
+  }
+  else if (!face_axis[2]) {
+    edge = 8 + 2 * quad_contact[3] + quad_contact[1];
+    quad_corner = 1;
+  }
+#endif
+  else {
+    corner =
+#ifdef P4_TO_P8
+      4 * quad_contact[5] +
+#endif
+      2 * quad_contact[3] + quad_contact[1];
+    quad_corner = 1;
+  }
+  if (quad_corner) {
+    /* Neighbor is across a tree edge or corner */
+    P4EST_ASSERT (exists_arr != NULL);
+    P4EST_ASSERT (face == -1 &&
+                  ((edge >= 0 && corner == -1) ||
+                   (edge == -1 && corner >= 0)));
+    if (corner >= 0) {
+      ta = &ci.corner_transforms;
+      sc_array_init (ta, sizeof (p4est_corner_transform_t));
+      p4est_find_corner_transform (conn, treeid, corner, &ci);
+    }
+#ifdef P4_TO_P8
+    else {
+      ta = &ei.edge_transforms;
+      sc_array_init (ta, sizeof (p8est_edge_transform_t));
+      p8est_find_edge_transform (conn, treeid, edge, &ei);
+    }
+#endif
+    sc_array_resize (exists_arr, ta->elem_count);
+
+    exists = 0;
+    for (ctreeidz = 0; ctreeidz < ta->elem_count; ++ctreeidz) {
+      if (corner >= 0) {
+        ct = p4est_corner_array_index (ta, ctreeidz);
+        tqtreeid = ct->ntree;
+        tq = *q;
+        p4est_quadrant_transform_corner (&tq, (int) ct->ncorner, 1);
+      }
+#ifdef P4_TO_P8
+      else {
+        et = p8est_edge_array_index (ta, ctreeidz);
+        tqtreeid = et->ntree;
+        p8est_quadrant_transform_edge (q, &tq, &ei, et, 1);
+      }
+      et = NULL;
+#endif
+      ct = NULL;
+
+      qproc = p4est_comm_find_owner (p4est, tqtreeid, &tq, rank);
+      if (qproc == rank) {
+        tqtree = p4est_tree_array_index (p4est->trees, tqtreeid);
+        lnid = sc_array_bsearch (&tqtree->quadrants, &tq,
+                                 p4est_quadrant_compare);
+      }
+      else {
+        lnid = p4est_ghost_bsearch (ghost, qproc, tqtreeid, &tq);
+        P4EST_ASSERT (lnid == -1 ||
+                      (ghost->proc_offsets[qproc] <= lnid &&
+                       lnid < ghost->proc_offsets[qproc + 1]));
+      }
+      if (rproc_arr != NULL) {
+        *(int *) sc_array_push (rproc_arr) = qproc;
+      }
+      if (rquad_arr != NULL) {
+        rquad = (p4est_quadrant_t *) sc_array_push (rquad_arr);
+        *rquad = tq;
+        rquad->p.piggy3.which_tree = tqtreeid;
+        rquad->p.piggy3.local_num = (p4est_locidx_t) lnid;
+      }
+
+      /* add the existence value */
+      pexists = (int *) sc_array_index (exists_arr, ctreeidz);
+      *pexists = (lnid != -1);
+      exists = exists || *pexists;
+    }
+
+    sc_array_reset (ta);
+    return exists;
+  }
+  else {
+    /* Neighbor is across a tree face */
+    P4EST_ASSERT (face >= 0 && edge == -1 && corner == -1);
+    P4EST_ASSERT (quad_contact[face]);
+
+    tqtreeid = p4est_find_face_transform (conn, treeid, face, ftransform);
+    if (tqtreeid == -1) {
+      /* there is no tree neighbor across this face */
+      return 0;
+    }
+    p4est_quadrant_transform_face (q, &tq, ftransform);
+
+    /* find owner of the transformed quadrant */
+    qproc = p4est_comm_find_owner (p4est, tqtreeid, &tq, rank);
+    if (qproc == rank) {
+      tqtree = p4est_tree_array_index (p4est->trees, tqtreeid);
+      lnid = sc_array_bsearch (&tqtree->quadrants, &tq,
+                               p4est_quadrant_compare);
+    }
+    else {
+      lnid = p4est_ghost_bsearch (ghost, qproc, tqtreeid, &tq);
+      P4EST_ASSERT (lnid == -1 ||
+                    (ghost->proc_offsets[qproc] <= lnid &&
+                     lnid < ghost->proc_offsets[qproc + 1]));
+    }
+    if (rproc_arr != NULL) {
+      *(int *) sc_array_push (rproc_arr) = qproc;
+    }
+    if (rquad_arr != NULL) {
+      rquad = (p4est_quadrant_t *) sc_array_push (rquad_arr);
+      *rquad = tq;
+      rquad->p.piggy3.which_tree = tqtreeid;
+      rquad->p.piggy3.local_num = (p4est_locidx_t) lnid;
+    }
+    return (lnid != -1);
+  }
+}
+
+p4est_locidx_t
+p4est_face_quadrant_exists (p4est_t * p4est, p4est_ghost_t * ghost,
+                            p4est_topidx_t treeid, const p4est_quadrant_t * q,
+                            int *pface, int *phang, int *owner_rank)
+{
+  const int           rank = p4est->mpirank;
+  int                 qproc;
+  int                 nface, face, orientation;
+  int                 ftransform[P4EST_FTRANSFORM];
+  ssize_t             lnid;
+  p4est_topidx_t      tqtreeid, tqtreeid2;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  p4est_quadrant_t    tq, non_existent;
+#ifdef P4_TO_P8
+  int                 face_ref, face_perm;
+#endif
+
+  face = *pface;
+  P4EST_ASSERT (treeid >= 0 && 0 <= face && face < P4EST_FACES);
+
+  P4EST_QUADRANT_INIT (&non_existent);
+  if (non_existent.level == q->level) {
+    return -1;
+  }
+
+  /* determine the hanging face number */
+  if (phang != NULL) {
+    P4EST_ASSERT (*phang >= 0 && *phang < P4EST_CHILDREN);
+    *phang = p4est_corner_face_corners[*phang][face];
+    P4EST_ASSERT (*phang >= 0 && *phang < P4EST_HALF);
+  }
+
+  /* q is in the unit domain */
+  if (p4est_quadrant_is_inside_root (q)) {
+    *pface = p4est_face_dual[face];
+    *owner_rank = qproc = p4est_comm_find_owner (p4est, treeid, q, rank);
+    if (qproc == rank) {
+      p4est_tree_t       *tree;
+
+      tree = p4est_tree_array_index (p4est->trees, treeid);
+      lnid = sc_array_bsearch (&tree->quadrants, q, p4est_quadrant_compare);
+      return (lnid == -1) ? (p4est_locidx_t) (-1) :
+        (tree->quadrants_offset + (p4est_locidx_t) lnid);
+    }
+    else {
+      lnid = p4est_ghost_bsearch (ghost, qproc, treeid, q);
+      return (lnid == -1) ? (p4est_locidx_t) (-1) :
+        (q = p4est_quadrant_array_index (&ghost->ghosts, (size_t) lnid),
+         q->p.piggy3.local_num);
+    }
+  }
+
+  /* neighbor is across a tree face */
+  tqtreeid = conn->tree_to_tree[P4EST_FACES * treeid + face];
+  nface = (int) conn->tree_to_face[P4EST_FACES * treeid + face];
+  if (tqtreeid == treeid && nface == face) {
+    *owner_rank = -1;
+    *pface = -1;
+    if (phang) {
+      *phang = -1;
+    }
+    return -2;
+  }
+
+  /* transform the hanging face number */
+  *pface = nface;
+  if (phang != NULL) {
+    orientation = nface / P4EST_FACES;
+#ifdef P4_TO_P8
+    face_ref = p8est_face_permutation_refs[face][nface % P4EST_FACES];
+    face_perm = p8est_face_permutation_sets[face_ref][orientation];
+    *phang = p8est_face_permutations[face_perm][*phang];
+#else
+    *phang = *phang ^ orientation;
+#endif
+  }
+
+  /* transform quadrant */
+  tqtreeid2 = p4est_find_face_transform (conn, treeid, face, ftransform);
+  P4EST_ASSERT (tqtreeid == tqtreeid2);
+  p4est_quadrant_transform_face (q, &tq, ftransform);
+
+  /* find its owner and local number */
+  *owner_rank = qproc = p4est_comm_find_owner (p4est, tqtreeid, &tq, rank);
+  if (qproc == rank) {
+    p4est_tree_t       *tqtree;
+
+    tqtree = p4est_tree_array_index (p4est->trees, tqtreeid);
+    lnid = sc_array_bsearch (&tqtree->quadrants, &tq, p4est_quadrant_compare);
+    return (lnid == -1) ? (p4est_locidx_t) (-1) :
+      (tqtree->quadrants_offset + (p4est_locidx_t) lnid);
+  }
+  else {
+    lnid = p4est_ghost_bsearch (ghost, qproc, tqtreeid, &tq);
+    return (lnid == -1) ? (p4est_locidx_t) (-1) :
+      (q =
+       p4est_quadrant_array_index (&ghost->ghosts, (size_t) lnid),
+       q->p.piggy3.local_num);
+  }
+}
+
+/** Checks if a quadrant's face is on the boundary of the forest.
+ *
+ * \param [in] p4est  The forest in which to search for \a q
+ * \param [in] treeid The tree to which \a q belongs.
+ * \param [in] q      The quadrant that is in question.
+ * \param [in] face   The face of the quadrant that is in question.
+ *
+ * \return true if the quadrant's face is on the boundary of the forest and
+ *         false otherwise.
+ */
+static int
+p4est_quadrant_on_face_boundary (p4est_t * p4est, p4est_topidx_t treeid,
+                                 int face, const p4est_quadrant_t * q)
+{
+  p4est_qcoord_t      dh, xyz;
+  p4est_connectivity_t *conn = p4est->connectivity;
+
+  P4EST_ASSERT (0 <= face && face < P4EST_FACES);
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  if (conn->tree_to_tree[P4EST_FACES * treeid + face] != treeid ||
+      (int) conn->tree_to_face[P4EST_FACES * treeid + face] != face) {
+    return 0;
+  }
+
+  dh = P4EST_LAST_OFFSET (q->level);
+  switch (face / 2) {
+  case 0:
+    xyz = q->x;
+    break;
+  case 1:
+    xyz = q->y;
+    break;
+#ifdef P4_TO_P8
+  case 2:
+    xyz = q->z;
+    break;
+#endif
+  default:
+    SC_ABORT_NOT_REACHED ();
+    break;
+  }
+  return xyz == ((face & 0x01) ? dh : 0);
+}
+
+/** Get the smallest corner neighbor of \a q.
+ *
+ * Gets the smallest corner neighbor, which is half of the size assuming the
+ * 2-1 constaint.
+ *
+ * \param [in]  q      The quadrant whose corner neighbor will be constructed.
+ * \param [in]  corner The corner across which to generate the neighbor.
+ * \param [out] n0     Filled with the smallest corner neighbor, which is
+ *                     half of the size assuming the 2-1 constaint.
+ * \param [out] n0ur   If not NULL, it is filled with smallest quadrant
+ *                     that fits in the upper right corner of \a n0.
+ */
+static void
+p4est_quadrant_get_half_corner_neighbor (const p4est_quadrant_t * q,
+                                         int corner,
+                                         p4est_quadrant_t * n0,
+                                         p4est_quadrant_t * n0ur)
+{
+  p4est_quadrant_half_corner_neighbor (q, corner, n0);
+  if (n0ur != NULL) {
+    const p4est_qcoord_t qh_2 = P4EST_QUADRANT_LEN (q->level + 1);
+    const p4est_qcoord_t dh = qh_2 - P4EST_QUADRANT_LEN (P4EST_QMAXLEVEL);
+
+    n0ur->x = n0->x + dh;
+    n0ur->y = n0->y + dh;
+#ifdef P4_TO_P8
+    n0ur->z = n0->z + dh;
+#endif
+    n0ur->level = P4EST_QMAXLEVEL;
+    P4EST_ASSERT (p4est_quadrant_is_extended (n0ur));
+  }
+}
+
+/** Get the possible corner neighbors of \a q.
+ *
+ * Gets the corner face neighbors, possible assuming the 2-1 constant.
+ * If the larger quadrant doesn't exist than it returned as initialized by
+ * P4EST_QUADRANT_INIT.
+ *
+ * \param [in]  q       The quadrant whose face neighbors will be constructed.
+ * \param [in]  corner  The corner across which to generate the neighbors.
+ * \param [out] n[0]    Filled with the possible half-size corner neighbor
+ *                      if it exists or initialized by P4EST_QUADRANT_INIT.
+ * \param [out] n[1]    Filled with the possible same-size corner neighbor.
+ * \param [out] n[2]    Filled with the possible double-size corner neighbor
+ *                      if it exists or initialized by P4EST_QUADRANT_INIT.
+ */
+static void
+p4est_quadrant_get_possible_corner_neighbors (const p4est_quadrant_t * q,
+                                              int corner,
+                                              p4est_quadrant_t n[])
+{
+  const int           qcid = p4est_quadrant_child_id (q);
+  p4est_quadrant_t   *r = &n[2];
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  if (q->level == P4EST_QMAXLEVEL) {
+    P4EST_QUADRANT_INIT (&n[0]);
+  }
+  else {
+    p4est_quadrant_get_half_corner_neighbor (q, corner, &n[0], NULL);
+  }
+
+  p4est_quadrant_corner_neighbor (q, corner, &n[1]);
+
+  /* Check to see if the larger neighbor exists */
+  if ((corner != qcid) || (q->level == 0)) {
+    P4EST_QUADRANT_INIT (r);
+  }
+  else {
+    p4est_quadrant_parent (q, r);
+    p4est_quadrant_corner_neighbor (r, corner, r);
+  }
+}
+
+/** Checks if a quadrant's corner is on the boundary of the forest.
+ *
+ * This means that the quadrant's tree doesn't have any non face neighbors.
+ *
+ * \param [in] p4est  The forest in which to search for \a q
+ * \param [in] treeid The tree to which \a q belongs.
+ * \param [in] q      The quadrant that is in question.
+ * \param [in] corner The corner of quadrant that is in question.
+ *
+ * \return true if the quadrant's corner is on the boundary of the forest and
+ *         false otherwise.
+ */
+static int
+p4est_quadrant_on_corner_boundary (p4est_t * p4est, p4est_topidx_t treeid,
+                                   int corner, const p4est_quadrant_t * q)
+{
+  p4est_connectivity_t *conn = p4est->connectivity;
+  int                 face;
+  int                 on_boundary = 0;
+  p4est_quadrant_t    q2;
+#ifdef P4_TO_P8
+  int                 edge;
+  p8est_edge_info_t   ei;
+  sc_array_t         *eta;
+#endif
+  p4est_corner_info_t ci;
+  sc_array_t         *cta;
+
+  P4EST_ASSERT (0 <= corner && corner < P4EST_CHILDREN);
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  if (p4est_quadrant_touches_corner (q, corner, 1)) {
+    cta = &ci.corner_transforms;
+    sc_array_init (cta, sizeof (p4est_corner_transform_t));
+    p4est_find_corner_transform (conn, treeid, corner, &ci);
+
+    on_boundary = (cta->elem_count == 0);
+    sc_array_reset (cta);
+
+    return on_boundary;
+  }
+
+  P4EST_QUADRANT_INIT (&q2);
+  p4est_quadrant_corner_neighbor (q, corner, &q2);
+  P4EST_ASSERT (!p4est_quadrant_is_outside_corner (&q2));
+
+#ifdef P4_TO_P8
+  if (p8est_quadrant_is_outside_edge_extra (&q2, &edge)) {
+    eta = &ei.edge_transforms;
+    sc_array_init (eta, sizeof (p8est_edge_transform_t));
+    p8est_find_edge_transform (conn, treeid, edge, &ei);
+
+    on_boundary = (eta->elem_count == 0);
+    sc_array_reset (eta);
+
+    return on_boundary;
+  }
+  P4EST_ASSERT (!p8est_quadrant_is_outside_edge (&q2));
+#endif
+
+  if (q2.x < 0) {
+    face = 0;
+  }
+  else if (q2.x >= P4EST_ROOT_LEN) {
+    face = 1;
+  }
+  else if (q2.y < 0) {
+    face = 2;
+  }
+  else if (q2.y >= P4EST_ROOT_LEN) {
+    face = 3;
+  }
+#ifdef P4_TO_P8
+  else if (q2.z < 0) {
+    face = 4;
+  }
+  else if (q2.z >= P4EST_ROOT_LEN) {
+    face = 5;
+  }
+#endif
+  else {
+    return 0;
+  }
+
+  return
+    (conn->tree_to_tree[P4EST_FACES * treeid + face] == treeid &&
+     (int) conn->tree_to_face[P4EST_FACES * treeid + face] == face);
+}
+
+int
+p4est_is_balanced (p4est_t * p4est, p4est_connect_type_t btype)
+{
+  int                 zero = 0;
+  int                 face, corner;
+  int                 ii, qcid;
+  int                *pe0, *pe1, *pe2;
+  int                 failed;
+  int                 e0, e1, e0b, e1b, e2, e3;
+  int                 bigger_face[P4EST_FACES];
+  size_t              cez, zz;
+  p4est_topidx_t      nt;
+  p4est_topidx_t      first_local_tree = p4est->first_local_tree;
+  p4est_topidx_t      last_local_tree = p4est->last_local_tree;
+  p4est_quadrant_t   *q;
+  p4est_quadrant_t    n[P4EST_HALF + 2];
+  p4est_tree_t       *tree;
+  sc_array_t         *quadrants;
+  sc_array_t          e0_a, e1_a, e2_a;
+#ifdef P4_TO_P8
+  int                 edge;
+  int                *pe3;
+  int                 bigger_edge[12];
+  sc_array_t          e3_a;
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              big_count[12];
+#endif
+#endif
+  p4est_ghost_t      *gl;
+
+  gl = p4est_ghost_new_check (p4est, btype, P4EST_GHOST_UNBALANCED_FAIL);
+  if (gl == NULL) {
+    return 0;
+  }
+
+  for (ii = 0; ii < P4EST_HALF + 2; ++ii) {
+    P4EST_QUADRANT_INIT (&n[ii]);
+  }
+
+  failed = 0;
+  sc_array_init (&e0_a, sizeof (int));
+  sc_array_init (&e1_a, sizeof (int));
+  sc_array_init (&e2_a, sizeof (int));
+#ifdef P4_TO_P8
+  sc_array_init (&e3_a, sizeof (int));
+#endif
+
+  /* loop over all local trees */
+  for (nt = first_local_tree; nt <= last_local_tree; ++nt) {
+    tree = p4est_tree_array_index (p4est->trees, nt);
+    quadrants = &tree->quadrants;
+
+    /* Find the neighboring processors of each quadrant */
+    for (zz = 0; zz < quadrants->elem_count; ++zz) {
+      q = p4est_quadrant_array_index (quadrants, zz);
+      qcid = p4est_quadrant_child_id (q);
+
+      /* Find face neighbors */
+      for (face = 0; face < P4EST_FACES; ++face) {
+        bigger_face[face] = 0;
+
+        /* If q is at a boundary then it is automatically balanced */
+        if (p4est_quadrant_on_face_boundary (p4est, nt, face, q)) {
+          continue;
+        }
+
+        /* Do more expensive face balance checks */
+        p4est_quadrant_all_face_neighbors (q, face, n);
+        e0 = p4est_quadrant_exists (p4est, gl, nt, &n[0], NULL, NULL, NULL);
+        e1 = p4est_quadrant_exists (p4est, gl, nt, &n[1], NULL, NULL, NULL);
+#ifndef P4_TO_P8
+        e0b = e1b = e0;
+#else
+        e0b = p4est_quadrant_exists (p4est, gl, nt, &n[2], NULL, NULL, NULL);
+        e1b = p4est_quadrant_exists (p4est, gl, nt, &n[3], NULL, NULL, NULL);
+#endif
+        if (e0 != e1 || e0 != e0b || e0 != e1b) {
+          P4EST_NOTICE ("Contradicting small face neighbors\n");
+          failed = 1;
+          goto failtest;
+        }
+        e2 = p4est_quadrant_exists (p4est, gl, nt, &n[P4EST_HALF],
+                                    NULL, NULL, NULL);
+        e3 = p4est_quadrant_exists (p4est, gl, nt, &n[P4EST_HALF + 1],
+                                    NULL, NULL, NULL);
+        if ((int) e0 + (int) e2 + (int) e3 != 1) {
+          P4EST_NOTICE ("Face balance quadrant mismatch\n");
+          failed = 1;
+          goto failtest;
+        }
+        bigger_face[face] = e3;
+      }
+
+      if (btype == P4EST_CONNECT_FACE)
+        continue;
+
+#ifdef P4_TO_P8
+
+      /* Find edge neighbors */
+      for (edge = 0; edge < P8EST_EDGES; ++edge) {
+        bigger_edge[edge] = 0;
+#ifdef P4EST_ENABLE_DEBUG
+        big_count[edge] = 0;
+#endif
+
+        /* If q is at a boundary then it is automatically balanced */
+        if (p8est_quadrant_on_edge_boundary (p4est, nt, edge, q)) {
+          continue;
+        }
+
+        /* Do more expensive edge balance checks */
+        p8est_quadrant_get_possible_edge_neighbors (q, edge, n);
+        e0 = p4est_quadrant_exists (p4est, gl, nt, &n[0], &e0_a, NULL, NULL);
+        e1 = p4est_quadrant_exists (p4est, gl, nt, &n[1], &e1_a, NULL, NULL);
+        if (e0 != e1 || e0_a.elem_count != e1_a.elem_count) {
+          P4EST_NOTICE ("Contradicting small edge neighbors\n");
+          failed = 1;
+          goto failtest;
+        }
+        e2 = p4est_quadrant_exists (p4est, gl, nt, &n[2], &e2_a, NULL, NULL);
+        e3 = p4est_quadrant_exists (p4est, gl, nt, &n[3], &e3_a, NULL, NULL);
+        P4EST_ASSERT (((e0_a.elem_count == 0 && q->level == P4EST_QMAXLEVEL)
+                       || e0_a.elem_count == e2_a.elem_count)
+                      && ((e3_a.elem_count == 0 && (!e3 || q->level == 0))
+                          || e3_a.elem_count == e2_a.elem_count));
+
+        face = p8est_child_edge_faces[qcid][edge];
+        if (face >= 0 && bigger_face[face]) {
+          P4EST_ASSERT (e2_a.elem_count == 0);
+          if (e0 || e2 || e3) {
+            P4EST_NOTICE ("Invalid edges across hanging face\n");
+            failed = 1;
+            goto failtest;
+          }
+        }
+        else {
+          if (!e0 && !e2 && !e3) {
+            P4EST_NOTICE ("Edge balance missing quadrants\n");
+            failed = 1;
+            goto failtest;
+          }
+          if (e2_a.elem_count == 0 && (int) e0 + (int) e2 + (int) e3 != 1) {
+            P4EST_NOTICE ("Edge balance duplicate quadrants\n");
+            failed = 1;
+            goto failtest;
+          }
+          for (cez = 0; cez < e2_a.elem_count; ++cez) {
+            pe0 = (e0_a.elem_count > 0) ?
+              (int *) sc_array_index (&e0_a, cez) : &zero;
+            pe1 = (e1_a.elem_count > 0) ?
+              (int *) sc_array_index (&e1_a, cez) : &zero;
+            pe2 = (int *) sc_array_index (&e2_a, cez);
+            pe3 = (e3_a.elem_count > 0) ?
+              (int *) sc_array_index (&e3_a, cez) : &zero;
+            if (*pe0 + *pe2 + *pe3 != 1 || *pe0 != *pe1) {
+              P4EST_NOTICE ("Edge balance quadrant mismatch\n");
+              failed = 1;
+              goto failtest;
+            }
+          }
+        }
+        bigger_edge[edge] = e3;
+#ifdef P4EST_ENABLE_DEBUG
+        big_count[edge] = e3_a.elem_count;
+#endif
+      }
+
+      if (btype == P8EST_CONNECT_EDGE)
+        continue;
+#endif
+
+      /* Find corner neighbors */
+      for (corner = 0; corner < P4EST_CHILDREN; ++corner) {
+
+        /* If q is at a boundary then it is automatically balanced */
+        if (p4est_quadrant_on_corner_boundary (p4est, nt, corner, q)) {
+          continue;
+        }
+
+        /* Do more expensive corner balance checks */
+        p4est_quadrant_get_possible_corner_neighbors (q, corner, n);
+        e0 = p4est_quadrant_exists (p4est, gl, nt, &n[0], &e0_a, NULL, NULL);
+        e1 = p4est_quadrant_exists (p4est, gl, nt, &n[1], &e1_a, NULL, NULL);
+        e2 = p4est_quadrant_exists (p4est, gl, nt, &n[2], &e2_a, NULL, NULL);
+        P4EST_ASSERT (((e0_a.elem_count == 0 && q->level == P4EST_QMAXLEVEL)
+                       || e0_a.elem_count == e1_a.elem_count)
+                      && ((e2_a.elem_count == 0 && (!e2 || q->level == 0))
+                          || e2_a.elem_count == e1_a.elem_count));
+
+        face = p4est_child_corner_faces[qcid][corner];
+#ifdef P4_TO_P8
+        edge = p8est_child_corner_edges[qcid][corner];
+#endif
+        if (face >= 0 && bigger_face[face]) {
+          P4EST_ASSERT (e1_a.elem_count == 0);
+          if (e0 || e1 || e2) {
+            P4EST_NOTICE ("Invalid corners across hanging face\n");
+            failed = 1;
+            goto failtest;
+          }
+        }
+#ifndef P4_TO_P8
+        else if (!e0 && !e1 && !e2) {
+          P4EST_NOTICE ("Corner balance missing quadrants\n");
+          failed = 1;
+          goto failtest;
+        }
+#endif
+        else if (e1_a.elem_count == 0) {
+#ifdef P4_TO_P8
+          if (edge >= 0 && bigger_edge[edge]) {
+            P4EST_ASSERT (big_count[edge] == 0);
+            if (e0 || e1 || e2) {
+              P4EST_NOTICE ("Invalid corners across hanging edge\n");
+              failed = 1;
+              goto failtest;
+            }
+          }
+          else
+#endif
+          if ((int) e0 + (int) e1 + (int) e2 != 1) {
+            P4EST_NOTICE ("Corner balance quadrant mismatch\n");
+            failed = 1;
+            goto failtest;
+          }
+        }
+        else {
+#ifdef P4_TO_P8
+          e3 = 0;
+          if (edge >= 0 && bigger_edge[edge]) {
+            P4EST_ASSERT (big_count[edge] == e1_a.elem_count);
+
+            /* recreate the edge neighbor information */
+            p4est_quadrant_parent (q, &n[3]);
+            p8est_quadrant_edge_neighbor (&n[3], edge, &n[3]);
+            e3 =
+              p4est_quadrant_exists (p4est, gl, nt, &n[3], &e3_a, NULL, NULL);
+            P4EST_ASSERT (e3 && big_count[edge] == e3_a.elem_count);
+          }
+#endif
+          for (cez = 0; cez < e1_a.elem_count; ++cez) {
+            pe0 = (e0_a.elem_count > 0) ?
+              (int *) sc_array_index (&e0_a, cez) : &zero;
+            pe1 = (int *) sc_array_index (&e1_a, cez);
+            pe2 = (e2_a.elem_count > 0) ?
+              (int *) sc_array_index (&e2_a, cez) : &zero;
+#ifdef P4_TO_P8
+            if (e3) {
+              pe3 = (int *) sc_array_index (&e3_a, cez);
+              if (*pe3) {
+                if (*pe0 || *pe1 || *pe2) {
+                  P4EST_NOTICE ("Invalid corners across hanging edge\n");
+                  failed = 1;
+                  goto failtest;
+                }
+                continue;
+              }
+            }
+#endif
+            if (*pe0 + *pe1 + *pe2 != 1) {
+              P4EST_NOTICE ("Corner balance quadrant mismatch\n");
+              failed = 1;
+              goto failtest;
+            }
+          }
+        }
+      }
+
+      P4EST_ASSERT (btype == P4EST_CONNECT_FULL);
+    }
+  }
+
+failtest:
+  sc_array_reset (&e0_a);
+  sc_array_reset (&e1_a);
+  sc_array_reset (&e2_a);
+#ifdef P4_TO_P8
+  sc_array_reset (&e3_a);
+#endif
+  p4est_ghost_destroy (gl);
+
+  return !p4est_comm_sync_flag (p4est, failed, sc_MPI_BOR);
+}
+
+static              size_t
+ghost_tree_type (sc_array_t * array, size_t zindex, void *data)
+{
+  p4est_quadrant_t   *q;
+
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_quadrant_t));
+
+  q = (p4est_quadrant_t *) sc_array_index (array, zindex);
+  return (size_t) q->p.which_tree;
+}
+
+#ifdef P4EST_ENABLE_MPI
+
+static              size_t
+ghost_proc_type (sc_array_t * array, size_t zindex, void *data)
+{
+  p4est_quadrant_t   *q;
+  p4est_t            *p4est = (p4est_t *) data;
+  int                 proc;
+
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_quadrant_t));
+
+  q = (p4est_quadrant_t *) sc_array_index (array, zindex);
+  proc = p4est_comm_find_owner (p4est, q->p.which_tree, q, 0);
+  P4EST_ASSERT (proc >= 0 && proc < p4est->mpisize);
+  P4EST_ASSERT (p4est_comm_is_owner (p4est, q->p.which_tree, q, proc));
+  return (size_t) proc;
+}
+
+/** This adds a quadrant to the end of a buffer.
+ *
+ * It crams the tree id into the user_data field of the quadrant in
+ * the buffer and only adds the quadrant to the end of the buffer if
+ * it is unique.
+ *
+ * \param [in,out] buf    \a q is added to the end if it is not already there.
+ * \param [in,out] q      the quadrant to be added.  The \c user_data field
+ *                        is filled with \a treeid.
+ * \param [in]            treeid the tree id of \a q.
+ * \return                true if the ghost was added, false if duplicate.
+ */
+static int
+p4est_add_ghost_to_buf (sc_array_t * buf, p4est_topidx_t treeid,
+                        p4est_locidx_t number, const p4est_quadrant_t * q)
+{
+  p4est_quadrant_t   *qold, *qnew;
+
+  P4EST_ASSERT (treeid >= 0 && number >= 0);
+
+  /* Check to see if the quadrant already is last in the array */
+  if (buf->elem_count > 0) {
+    qold = p4est_quadrant_array_index (buf, buf->elem_count - 1);
+    if (treeid == qold->p.piggy3.which_tree &&
+        p4est_quadrant_is_equal (q, qold)) {
+      return 0;
+    }
+  }
+
+  qnew = p4est_quadrant_array_push (buf);
+  *qnew = *q;
+
+  /* Cram the tree id and the local number into the user_data pointer */
+  qnew->p.piggy3.which_tree = treeid;
+  qnew->p.piggy3.local_num = number;
+
+  return 1;
+}
+
+/** Data structure that contains temporary mirror information */
+typedef struct p4est_ghost_mirror
+{
+  int                 mpisize, mpirank;
+  int                 known;    /* was this mirror added before? */
+  p4est_locidx_t      sum_all_procs;    /* sum of mirrors by processor */
+  sc_array_t         *send_bufs;        /* lives in p4est_ghost_new_check */
+  sc_array_t         *mirrors;  /* lives in p4est_ghost_t */
+  sc_array_t         *offsets_by_proc;  /* a p4est_locidx_t array per proc */
+}
+p4est_ghost_mirror_t;
+
+/** Initialize temporary mirror storage */
+static void
+p4est_ghost_mirror_init (p4est_ghost_t * ghost, int mpirank,
+                         sc_array_t * send_bufs, p4est_ghost_mirror_t * m)
+{
+  int                 p;
+
+  m->mpisize = ghost->mpisize;
+  m->mpirank = mpirank;
+  /* m->known is left undefined: it needs to be set to 0 for every quadrant */
+  m->sum_all_procs = 0;
+
+  m->send_bufs = send_bufs;
+  P4EST_ASSERT (m->send_bufs->elem_size == sizeof (sc_array_t));
+  P4EST_ASSERT (m->send_bufs->elem_count == (size_t) m->mpisize);
+
+  m->mirrors = &ghost->mirrors;
+  P4EST_ASSERT (m->mirrors->elem_size == sizeof (p4est_quadrant_t));
+  P4EST_ASSERT (m->mirrors->elem_count == 0);
+
+  m->offsets_by_proc = P4EST_ALLOC (sc_array_t, ghost->mpisize);
+  for (p = 0; p < ghost->mpisize; ++p) {
+    sc_array_init (m->offsets_by_proc + p, sizeof (p4est_locidx_t));
+  }
+}
+
+/** Potentially record a quadrant that is to be sent as a mirror
+ * \param [in] m      The temporary data structure to work on.
+ * \param [in] treeid The tree number looped through by the current rank.
+ * \param [in] q      The quadrant currently looked at by current rank.
+ * \param [in] p      The rank that \a q should be sent to.
+ */
+static void
+p4est_ghost_mirror_add (p4est_ghost_mirror_t * m, p4est_topidx_t treeid,
+                        p4est_locidx_t number, p4est_quadrant_t * q, int p)
+{
+  sc_array_t         *buf;
+  p4est_locidx_t     *num;
+  p4est_quadrant_t   *qnew;
+
+  P4EST_ASSERT (p != m->mpirank);
+  P4EST_ASSERT (0 <= p && p < m->mpisize);
+
+  if (!m->known) {
+    /* add this quadrant to the mirror array */
+    qnew = p4est_quadrant_array_push (m->mirrors);
+    *qnew = *q;
+
+    /* cram the tree id and the local number into the user_data pointer */
+    qnew->p.piggy3.which_tree = treeid;
+    qnew->p.piggy3.local_num = number;
+
+    m->known = 1;
+  }
+
+  buf = p4est_ghost_array_index (m->send_bufs, p);
+  if (p4est_add_ghost_to_buf (buf, treeid, number, q)) {
+    P4EST_ASSERT (m->mirrors->elem_count > 0);
+
+    num = (p4est_locidx_t *) sc_array_push (m->offsets_by_proc + p);
+    *num = (p4est_locidx_t) (m->mirrors->elem_count - 1);
+    ++m->sum_all_procs;
+  }
+}
+
+/** Populate the mirror fields in the ghost layer with final data.
+ * The elements in the temporary p4est_ghost_mirror_t structure are freed. */
+static void
+p4est_ghost_mirror_reset (p4est_ghost_t * ghost, p4est_ghost_mirror_t * m,
+                          int populate)
+{
+  int                 p;
+  p4est_locidx_t     *mpm;
+  p4est_locidx_t      pcount, sum_all_procs = 0;
+
+  P4EST_ASSERT (ghost->mirror_proc_mirrors == NULL);
+
+  /* if we did not run into failtest, populate the mirrors */
+  if (populate) {
+    mpm = ghost->mirror_proc_mirrors =
+      P4EST_ALLOC (p4est_locidx_t, m->sum_all_procs);
+    for (p = 0; p < ghost->mpisize; ++p) {
+      pcount = (p4est_locidx_t) m->offsets_by_proc[p].elem_count;
+      P4EST_ASSERT (p != m->mpirank || pcount == 0);
+      memcpy (mpm + sum_all_procs, m->offsets_by_proc[p].array,
+              pcount * sizeof (p4est_locidx_t));
+      ghost->mirror_proc_offsets[p] = sum_all_procs;
+      sum_all_procs += pcount;
+    }
+    P4EST_ASSERT (sum_all_procs == m->sum_all_procs);
+    ghost->mirror_proc_offsets[p] = sum_all_procs;
+  }
+
+  /* clean up memory regardless */
+  for (p = 0; p < ghost->mpisize; ++p) {
+    sc_array_reset (m->offsets_by_proc + p);
+  }
+  P4EST_FREE (m->offsets_by_proc);
+  memset (m, 0, sizeof (p4est_ghost_mirror_t));
+}
+
+static void
+p4est_ghost_test_add (p4est_t * p4est, p4est_ghost_mirror_t * m,
+                      p4est_quadrant_t * q, p4est_topidx_t t,
+                      p4est_quadrant_t * nq, p4est_topidx_t nt,
+                      int32_t touch, int rank,
+#if 0
+                      sc_array_t * send_bufs,
+#endif
+                      p4est_locidx_t local_num)
+{
+  p4est_quadrant_t    temp;
+  p4est_quadrant_t   *lq, *uq;
+  int64_t             next_lid, uid;
+  int                 n0_proc, n1_proc, proc;
+  p4est_quadrant_t   *gfp = p4est->global_first_position;
+#if 0
+  sc_array_t         *buf;
+#endif
+  int32_t             rb;
+
+  P4EST_ASSERT (q->level == nq->level);
+  n0_proc = p4est_comm_find_owner (p4est, nt, nq, rank);
+  P4EST_ASSERT (n0_proc >= 0);
+  if (q->level == P4EST_QMAXLEVEL) {
+    if (n0_proc != rank) {
+#if 0
+      buf = p4est_ghost_array_index (send_bufs, n0_proc);
+      p4est_add_ghost_to_buf (buf, t, local_num, q);
+#endif
+      p4est_ghost_mirror_add (m, t, local_num, q, n0_proc);
+    }
+    return;
+  }
+  p4est_quadrant_last_descendant (nq, &temp, P4EST_QMAXLEVEL);
+  n1_proc = p4est_comm_find_owner (p4est, nt, &temp, n0_proc);
+  P4EST_ASSERT (n1_proc >= n0_proc);
+  if (n0_proc == n1_proc) {
+    if (n0_proc != rank) {
+#if 0
+      buf = p4est_ghost_array_index (send_bufs, n0_proc);
+      p4est_add_ghost_to_buf (buf, t, local_num, q);
+#endif
+      p4est_ghost_mirror_add (m, t, local_num, q, n0_proc);
+    }
+    return;
+  }
+  for (proc = n0_proc; proc <= n1_proc; proc++) {
+    if (proc == rank) {
+      continue;
+    }
+    lq = &(gfp[proc]);
+    uq = &(gfp[proc + 1]);
+    /* check for empty processor */
+    if (p4est_quadrant_is_equal_piggy (lq, uq)) {
+      continue;
+    }
+    if (proc == n0_proc) {
+      lq = NULL;
+    }
+    else {
+      P4EST_ASSERT (p4est_quadrant_is_valid (lq));
+      P4EST_ASSERT (lq->p.which_tree == nt);
+      P4EST_ASSERT (p4est_quadrant_is_ancestor (nq, lq) ||
+                    p4est_quadrant_is_equal (nq, lq));
+    }
+    if (proc == n1_proc) {
+      uq = NULL;
+    }
+    else {
+      P4EST_ASSERT (p4est_quadrant_is_valid (uq));
+      P4EST_ASSERT (uq->p.which_tree == nt);
+      P4EST_ASSERT (p4est_quadrant_is_ancestor (nq, uq) ||
+                    p4est_quadrant_is_equal (nq, uq));
+      next_lid = p4est_quadrant_linear_id (uq, P4EST_QMAXLEVEL);
+      P4EST_ASSERT (next_lid > 0);
+      uid = next_lid - 1;
+      uq = &temp;
+      p4est_quadrant_set_morton (uq, P4EST_QMAXLEVEL, uid);
+      P4EST_ASSERT (p4est_quadrant_is_valid (uq));
+    }
+#ifdef P4EST_ENABLE_DEBUG
+    if (lq != NULL && uq != NULL) {
+      P4EST_ASSERT (p4est_quadrant_compare (lq, uq) <= 0);
+    }
+#endif
+    rb = p4est_find_range_boundaries (lq, uq, (int) q->level,
+#ifdef P4_TO_P8
+                                      NULL,
+#endif
+                                      NULL, NULL);
+    if (rb & touch) {
+#if 0
+      buf = p4est_ghost_array_index (send_bufs, proc);
+      p4est_add_ghost_to_buf (buf, t, local_num, q);
+#endif
+      p4est_ghost_mirror_add (m, t, local_num, q, proc);
+    }
+  }
+}
+
+#endif /* P4EST_ENABLE_MPI */
+
+static p4est_ghost_t *
+p4est_ghost_new_check (p4est_t * p4est, p4est_connect_type_t btype,
+                       p4est_ghost_tolerance_t tol)
+{
+  const p4est_topidx_t num_trees = p4est->connectivity->num_trees;
+  const int           num_procs = p4est->mpisize;
+#ifdef P4EST_ENABLE_MPI
+  const int           rank = p4est->mpirank;
+  MPI_Comm            comm = p4est->mpicomm;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  int                 face, corner;
+  int                 nface, ncheck, ncount;
+  int                 i;
+  int                 n0_proc, n0ur_proc, n1_proc;
+  int                 num_peers, peer, peer_proc;
+  int                 mpiret;
+  int                 maxed, failed;
+  int                 full_tree[2], tree_contact[2 * P4EST_DIM];
+  int                 urg[P4EST_DIM - 1];
+  size_t              pz, zz;
+  p4est_topidx_t      first_local_tree = p4est->first_local_tree;
+  p4est_topidx_t      last_local_tree = p4est->last_local_tree;
+#ifdef P4EST_ENABLE_DEBUG
+  p4est_locidx_t      li;
+#endif
+  p4est_locidx_t      local_num;
+  p4est_locidx_t      num_ghosts, ghost_offset, skipped;
+  p4est_locidx_t     *send_counts, *recv_counts;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *q;
+  p4est_quadrant_t    n[P4EST_HALF], nur[P4EST_HALF];
+  sc_array_t          send_bufs;
+  sc_array_t          procs[P4EST_DIM - 1];
+  sc_array_t         *buf, *quadrants;
+  MPI_Request        *recv_request, *send_request;
+  MPI_Request        *recv_load_request, *send_load_request;
+#ifdef P4_TO_P8
+  int                 edge, nedge;
+  p8est_edge_info_t   ei;
+  p8est_edge_transform_t *et;
+  sc_array_t         *eta;
+  size_t              etree;
+  int                 o, ref, set;
+  int                 c0, c1;
+  int                 nc0, nc1;
+  int                 oppedge;
+  int                 n1ur_proc;
+#endif
+#ifdef P4EST_ENABLE_DEBUG
+  p4est_quadrant_t   *q2;
+#endif
+  int                 ftransform[P4EST_FTRANSFORM];
+  int32_t             touch;
+  p4est_topidx_t      nnt;
+  p4est_corner_info_t ci;
+  p4est_corner_transform_t *ct;
+  sc_array_t         *cta;
+  size_t              ctree;
+  p4est_ghost_mirror_t m;
+#endif
+  size_t             *ppz;
+  sc_array_t          split;
+  sc_array_t         *ghost_layer;
+  p4est_topidx_t      nt;
+  p4est_ghost_t      *gl;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING "_ghost_new %s\n",
+                            p4est_connect_type_string (btype));
+  p4est_log_indent_push ();
+
+  gl = P4EST_ALLOC (p4est_ghost_t, 1);
+  gl->mpisize = num_procs;
+  gl->num_trees = num_trees;
+  gl->btype = btype;
+
+  ghost_layer = &gl->ghosts;
+  sc_array_init (ghost_layer, sizeof (p4est_quadrant_t));
+  gl->tree_offsets = P4EST_ALLOC (p4est_locidx_t, num_trees + 1);
+  gl->proc_offsets = P4EST_ALLOC (p4est_locidx_t, num_procs + 1);
+
+  sc_array_init (&gl->mirrors, sizeof (p4est_quadrant_t));
+  gl->mirror_tree_offsets = P4EST_ALLOC (p4est_locidx_t, num_trees + 1);
+  gl->mirror_proc_mirrors = NULL;
+  gl->mirror_proc_offsets = P4EST_ALLOC (p4est_locidx_t, num_procs + 1);
+  gl->mirror_proc_fronts = NULL;
+  gl->mirror_proc_front_offsets = NULL;
+
+  gl->proc_offsets[0] = 0;
+  gl->mirror_proc_offsets[0] = 0;
+#ifndef P4EST_ENABLE_MPI
+  gl->proc_offsets[1] = 0;
+  gl->mirror_proc_offsets[1] = 0;
+#else
+#ifdef P4_TO_P8
+  eta = &ei.edge_transforms;
+#endif
+  cta = &ci.corner_transforms;
+
+  for (i = 0; i < P4EST_HALF; ++i) {
+    P4EST_QUADRANT_INIT (&n[i]);
+    P4EST_QUADRANT_INIT (&nur[i]);
+  }
+
+  failed = 0;
+  for (i = 0; i < P4EST_DIM - 1; ++i) {
+    sc_array_init (&procs[i], sizeof (int));
+  }
+  skipped = 0;
+
+  /* allocate empty send buffers */
+  sc_array_init (&send_bufs, sizeof (sc_array_t));
+  sc_array_resize (&send_bufs, (size_t) num_procs);
+  for (i = 0; i < num_procs; ++i) {
+    buf = p4est_ghost_array_index (&send_bufs, i);
+    sc_array_init (buf, sizeof (p4est_quadrant_t));
+  }
+
+  /* initialize structure to keep track of mirror quadrants */
+  p4est_ghost_mirror_init (gl, p4est->mpirank, &send_bufs, &m);
+
+  /* loop over all local trees */
+  local_num = 0;
+  for (nt = 0; nt < first_local_tree; ++nt) {
+    /* does nothing if this processor is empty */
+    gl->mirror_tree_offsets[nt] = 0;
+  }
+  for (nt = first_local_tree; nt <= last_local_tree; ++nt) {
+    /* does nothing if this processor is empty */
+    tree = p4est_tree_array_index (p4est->trees, nt);
+    quadrants = &tree->quadrants;
+    p4est_comm_tree_info (p4est, nt, full_tree, tree_contact, NULL, NULL);
+    gl->mirror_tree_offsets[nt] = (p4est_locidx_t) gl->mirrors.elem_count;
+
+    /* Find the smaller neighboring processors of each quadrant */
+    for (zz = 0; zz < quadrants->elem_count; ++local_num, ++zz) {
+      q = p4est_quadrant_array_index (quadrants, zz);
+      m.known = 0;
+
+      if (p4est_comm_neighborhood_owned
+          (p4est, nt, full_tree, tree_contact, q)) {
+        /* The 3x3 neighborhood of q is owned by this processor */
+        ++skipped;
+        continue;
+      }
+
+      /* Find smaller face neighbors */
+      for (face = 0; face < 2 * P4EST_DIM; ++face) {
+        if (tol < P4EST_GHOST_UNBALANCED_ALLOW) {
+          if (q->level == P4EST_QMAXLEVEL) {
+            p4est_quadrant_face_neighbor (q, face, &n[0]);
+            ncheck = 0;
+            ncount = 1;
+          }
+          else {
+            p4est_quadrant_half_face_neighbors (q, face, n, nur);
+            ncheck = ncount = P4EST_HALF;
+          }
+
+          n1_proc = -1;
+          for (i = 0; i < ncount; ++i) {
+            n0_proc = p4est_quadrant_find_owner (p4est, nt, face, &n[i]);
+            if (i < ncheck) {
+              /* Note that we will always check this
+               * because it prevents deadlocks
+               */
+              n0ur_proc = p4est_quadrant_find_owner (p4est, nt, face,
+                                                     &nur[i]);
+              if (n0_proc != n0ur_proc) {
+                P4EST_NOTICE ("Small face owner inconsistency\n");
+                failed = 1;
+                goto failtest;
+              }
+            }
+
+            if (n0_proc != rank && n0_proc >= 0 && n0_proc != n1_proc) {
+#if 0
+              buf = p4est_ghost_array_index (&send_bufs, n0_proc);
+              p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+              p4est_ghost_mirror_add (&m, nt, local_num, q, n0_proc);
+              n1_proc = n0_proc;
+            }
+          }
+        }
+        else {
+          p4est_quadrant_face_neighbor (q, face, &n[0]);
+          if (p4est_quadrant_is_inside_root (&n[0])) {
+            nface = face ^ 1;
+            touch = ((int32_t) 1 << nface);
+            p4est_ghost_test_add (p4est, &m, q, nt, &n[0], nt, touch, rank,
+                                  local_num);
+          }
+          else {
+            nnt = p4est_find_face_transform (conn, nt, face, ftransform);
+            if (nnt < 0) {
+              continue;
+            }
+            nface = (int) conn->tree_to_face[nt * P4EST_FACES + face];
+            nface %= P4EST_FACES;
+            touch = ((int32_t) 1 << nface);
+            p4est_quadrant_transform_face (&n[0], &n[1], ftransform);
+            p4est_ghost_test_add (p4est, &m, q, nt, &n[1], nnt, touch, rank,
+                                  local_num);
+          }
+        }
+      }
+
+      if (btype == P4EST_CONNECT_FACE)
+        continue;
+
+#ifdef P4_TO_P8
+
+      /* Find smaller edge neighbors */
+      for (edge = 0; edge < 12; ++edge) {
+        if (tol < P4EST_GHOST_UNBALANCED_ALLOW) {
+          if (q->level == P4EST_QMAXLEVEL) {
+            p8est_quadrant_edge_neighbor (q, edge, &n[0]);
+            maxed = 1;
+          }
+          else {
+            p8est_quadrant_get_half_edge_neighbors (q, edge, n, nur);
+            maxed = 0;
+          }
+
+          /* Check to see if we are a tree edge neighbor */
+          P4EST_ASSERT (!p4est_quadrant_is_outside_corner (&n[0]));
+          if (p8est_quadrant_is_outside_edge (&n[0])) {
+            p8est_quadrant_find_tree_edge_owners (p4est, nt, edge,
+                                                  &n[0], &procs[0], &urg[0]);
+            if (!maxed) {
+              p8est_quadrant_find_tree_edge_owners (p4est, nt, edge,
+                                                    &n[1], &procs[1],
+                                                    &urg[1]);
+              P4EST_ASSERT (procs[0].elem_count == procs[1].elem_count);
+
+              if (!urg[0] || !urg[1]) {
+                P4EST_NOTICE ("Tree edge owner inconsistency\n");
+                failed = 1;
+                goto failtest;
+              }
+            }
+
+            /* Then we have to loop over multiple neighbors */
+            for (pz = 0; pz < procs[0].elem_count; ++pz) {
+              n0_proc = *((int *) sc_array_index (&procs[0], pz));
+
+              if (n0_proc != rank) {
+#if 0
+                buf = p4est_ghost_array_index (&send_bufs, n0_proc);
+                p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+                p4est_ghost_mirror_add (&m, nt, local_num, q, n0_proc);
+              }
+
+              if (!maxed) {
+                n1_proc = *((int *) sc_array_index (&procs[1], pz));
+
+                if (n1_proc != n0_proc && n1_proc != rank) {
+#if 0
+                  buf = p4est_ghost_array_index (&send_bufs, n1_proc);
+                  p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+                  p4est_ghost_mirror_add (&m, nt, local_num, q, n1_proc);
+                }
+              }
+            }
+          }
+          else {
+            /* We are not at a tree edge so we only have two neighbors
+             * either inside the tree or across a face
+             */
+            n0_proc = n1_proc =
+              p4est_quadrant_find_owner (p4est, nt, -1, &n[0]);
+            if (!maxed) {
+              n1_proc = p4est_quadrant_find_owner (p4est, nt, -1, &n[1]);
+              n0ur_proc = p4est_quadrant_find_owner (p4est, nt, -1, &nur[0]);
+              n1ur_proc = p4est_quadrant_find_owner (p4est, nt, -1, &nur[1]);
+
+              /* Note that we will always check this
+               * because it prevents deadlocks
+               */
+              if (n0_proc != n0ur_proc || n1_proc != n1ur_proc) {
+                P4EST_NOTICE ("Small edge owner inconsistency\n");
+                failed = 1;
+                goto failtest;
+              }
+            }
+
+            if (n0_proc != rank && n0_proc >= 0) {
+#if 0
+              buf = p4est_ghost_array_index (&send_bufs, n0_proc);
+              p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+              p4est_ghost_mirror_add (&m, nt, local_num, q, n0_proc);
+            }
+
+            if (n1_proc != n0_proc && n1_proc != rank && n1_proc >= 0) {
+#if 0
+              buf = p4est_ghost_array_index (&send_bufs, n1_proc);
+              p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+              p4est_ghost_mirror_add (&m, nt, local_num, q, n1_proc);
+            }
+          }
+        }
+        else {
+          p8est_quadrant_edge_neighbor (q, edge, &n[0]);
+          if (p4est_quadrant_is_inside_root (&n[0])) {
+            nedge = edge ^ 3;
+            touch = ((int32_t) 1 << (6 + nedge));
+            p4est_ghost_test_add (p4est, &m, q, nt, &n[0], nt, touch, rank,
+                                  local_num);
+          }
+          else if (p4est_quadrant_is_outside_face (&n[0])) {
+            P4EST_ASSERT (p4est_quadrant_is_extended (&n[0]));
+            face = -1;
+            if (n[0].x < 0 || n[0].x >= P4EST_ROOT_LEN) {
+              face = p8est_edge_faces[edge][0];
+            }
+            else if (n[0].z < 0 || n[0].z >= P4EST_ROOT_LEN) {
+              face = p8est_edge_faces[edge][1];
+            }
+            else if (n[0].y < 0) {
+              face = 2;
+            }
+            else {
+              face = 3;
+            }
+            nnt = p4est_find_face_transform (conn, nt, face, ftransform);
+            if (nnt < 0) {
+              continue;
+            }
+            P4EST_ASSERT (face >= 0);
+            P4EST_ASSERT (p8est_edge_face_corners[edge][face][0] != -1);
+            if (p8est_edge_faces[edge][0] == face) {
+              oppedge = edge ^ 2;
+              P4EST_ASSERT (p8est_edge_faces[oppedge][0] == face);
+            }
+            else {
+              P4EST_ASSERT (p8est_edge_faces[edge][1] == face);
+              oppedge = edge ^ 1;
+              P4EST_ASSERT (p8est_edge_faces[oppedge][1] == face);
+            }
+            nface = (int) conn->tree_to_face[nt * P4EST_FACES + face];
+            o = nface / P4EST_FACES;
+            nface %= P4EST_FACES;
+            ref = p8est_face_permutation_refs[face][nface];
+            set = p8est_face_permutation_sets[ref][o];
+            c0 = p8est_edge_face_corners[oppedge][face][0];
+            c1 = p8est_edge_face_corners[oppedge][face][1];
+            nc0 = p8est_face_permutations[set][c0];
+            nc1 = p8est_face_permutations[set][c1];
+            nc0 = p8est_face_corners[nface][nc0];
+            nc1 = p8est_face_corners[nface][nc1];
+            nedge = p8est_child_corner_edges[nc0][nc1];
+            touch = ((int32_t) 1 << (6 + nedge));
+            p4est_quadrant_transform_face (&n[0], &n[1], ftransform);
+            p4est_ghost_test_add (p4est, &m, q, nt, &n[1], nnt, touch, rank,
+                                  local_num);
+          }
+          else {
+            P4EST_ASSERT (p8est_quadrant_is_outside_edge (&n[0]));
+            sc_array_init (eta, sizeof (p8est_edge_transform_t));
+            p8est_find_edge_transform (conn, nt, edge, &ei);
+            for (etree = 0; etree < eta->elem_count; etree++) {
+              et = p8est_edge_array_index (eta, etree);
+              p8est_quadrant_transform_edge (&n[0], &n[1], &ei, et, 1);
+              nnt = et->ntree;
+              nedge = (int) et->nedge;
+              touch = ((int32_t) 1 << (6 + nedge));
+              p4est_ghost_test_add (p4est, &m, q, nt, &n[1], nnt, touch, rank,
+                                    local_num);
+            }
+            sc_array_reset (eta);
+          }
+        }
+      }
+
+      if (btype == P8EST_CONNECT_EDGE)
+        continue;
+#endif
+
+      /* Find smaller corner neighbors */
+      for (corner = 0; corner < P4EST_CHILDREN; ++corner) {
+        if (tol < P4EST_GHOST_UNBALANCED_ALLOW) {
+          if (q->level == P4EST_QMAXLEVEL) {
+            p4est_quadrant_corner_neighbor (q, corner, &n[0]);
+            maxed = 1;
+          }
+          else {
+            p4est_quadrant_get_half_corner_neighbor (q, corner, &n[0],
+                                                     &nur[0]);
+            maxed = 0;
+          }
+
+          /* Check to see if we are a tree corner neighbor */
+          if (p4est_quadrant_is_outside_corner (&n[0])) {
+            /* Then we have to loop over multiple corner neighbors */
+            p4est_quadrant_find_tree_corner_owners (p4est, nt, corner, &n[0],
+                                                    &procs[0], &urg[0]);
+            if (!urg[0]) {
+              P4EST_NOTICE ("Tree corner owner inconsistency\n");
+              failed = 1;
+              goto failtest;
+            }
+
+            for (pz = 0; pz < procs[0].elem_count; ++pz) {
+              n0_proc = *((int *) sc_array_index (&procs[0], pz));
+
+              if (n0_proc != rank) {
+#if 0
+                buf = p4est_ghost_array_index (&send_bufs, n0_proc);
+                p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+                p4est_ghost_mirror_add (&m, nt, local_num, q, n0_proc);
+              }
+            }
+          }
+#ifdef P4_TO_P8
+          /* Check to see if we are a tree edge neighbor */
+          else if (p8est_quadrant_is_outside_edge_extra (&n[0], &edge)) {
+            p8est_quadrant_find_tree_edge_owners (p4est, nt, edge,
+                                                  &n[0], &procs[0], &urg[0]);
+            if (!urg[0]) {
+              P4EST_NOTICE ("Tree corner/edge owner inconsistency\n");
+              failed = 1;
+              goto failtest;
+            }
+
+            /* Then we have to loop over multiple edge neighbors */
+            for (pz = 0; pz < procs[0].elem_count; ++pz) {
+              n0_proc = *((int *) sc_array_index (&procs[0], pz));
+
+              if (n0_proc != rank) {
+#if 0
+                buf = p4est_ghost_array_index (&send_bufs, n0_proc);
+                p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+                p4est_ghost_mirror_add (&m, nt, local_num, q, n0_proc);
+              }
+            }
+          }
+#endif
+          else {
+            /* We are not at a tree edge or corner so
+             * we only have one corner neighbor
+             */
+            n0_proc = p4est_quadrant_find_owner (p4est, nt, -1, &n[0]);
+            if (!maxed) {
+              n0ur_proc = p4est_quadrant_find_owner (p4est, nt, -1, &nur[0]);
+
+              /* Note that we will always check this
+               * because it prevents deadlocks
+               */
+              if (n0_proc != n0ur_proc) {
+                P4EST_NOTICE ("Small corner owner inconsistency\n");
+                failed = 1;
+                goto failtest;
+              }
+            }
+
+            if (n0_proc != rank && n0_proc >= 0) {
+#if 0
+              buf = p4est_ghost_array_index (&send_bufs, n0_proc);
+              p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+              p4est_ghost_mirror_add (&m, nt, local_num, q, n0_proc);
+            }
+          }
+        }
+        else {
+          p4est_quadrant_corner_descendant (q, &n[1], corner,
+                                            P4EST_QMAXLEVEL);
+          p4est_quadrant_corner_neighbor (&n[1], corner, &n[0]);
+          if (p4est_quadrant_is_inside_root (&n[0])) {
+            n0_proc = p4est_comm_find_owner (p4est, nt, &n[0], rank);
+            P4EST_ASSERT (n0_proc >= 0);
+            if (n0_proc != rank) {
+#if 0
+              buf = p4est_ghost_array_index (&send_bufs, n0_proc);
+              p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+              p4est_ghost_mirror_add (&m, nt, local_num, q, n0_proc);
+            }
+          }
+          else if (p4est_quadrant_is_outside_face (&n[0])) {
+            if (n[0].x < 0 || n[0].x >= P4EST_ROOT_LEN) {
+              face = p4est_corner_faces[corner][0];
+            }
+#ifdef P4_TO_P8
+            else if (n[0].y < 0 || n[0].y >= P4EST_ROOT_LEN) {
+              face = p4est_corner_faces[corner][1];
+            }
+#endif
+            else {
+              face = p4est_corner_faces[corner][P4EST_DIM - 1];
+            }
+            nnt = p4est_find_face_transform (conn, nt, face, ftransform);
+            if (nnt < 0) {
+              continue;
+            }
+            p4est_quadrant_transform_face (&n[0], &n[1], ftransform);
+            n0_proc = p4est_comm_find_owner (p4est, nnt, &n[1], rank);
+            if (n0_proc != rank) {
+#if 0
+              buf = p4est_ghost_array_index (&send_bufs, n0_proc);
+              p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+              p4est_ghost_mirror_add (&m, nt, local_num, q, n0_proc);
+            }
+          }
+#ifdef P4_TO_P8
+          else if (p8est_quadrant_is_outside_edge_extra (&n[0], &edge)) {
+            sc_array_init (eta, sizeof (p8est_edge_transform_t));
+            p8est_find_edge_transform (conn, nt, edge, &ei);
+            for (etree = 0; etree < eta->elem_count; etree++) {
+              et = p8est_edge_array_index (eta, etree);
+              p8est_quadrant_transform_edge (&n[0], &n[1], &ei, et, 1);
+              nnt = et->ntree;
+              n0_proc = p4est_comm_find_owner (p4est, nnt, &n[1], rank);
+              if (n0_proc != rank) {
+#if 0
+                buf = p4est_ghost_array_index (&send_bufs, n0_proc);
+                p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+                p4est_ghost_mirror_add (&m, nt, local_num, q, n0_proc);
+              }
+            }
+            sc_array_reset (eta);
+          }
+#endif
+          else {
+            sc_array_init (cta, sizeof (p4est_corner_transform_t));
+            p4est_find_corner_transform (conn, nt, corner, &ci);
+            for (ctree = 0; ctree < cta->elem_count; ++ctree) {
+              ct = p4est_corner_array_index (cta, ctree);
+              p4est_quadrant_transform_corner (&n[0], (int) ct->ncorner, 1);
+              nnt = ct->ntree;
+              n0_proc = p4est_comm_find_owner (p4est, nnt, &n[0], rank);
+              if (n0_proc != rank) {
+#if 0
+                buf = p4est_ghost_array_index (&send_bufs, n0_proc);
+                p4est_add_ghost_to_buf (buf, nt, local_num, q);
+#endif
+                p4est_ghost_mirror_add (&m, nt, local_num, q, n0_proc);
+              }
+            }
+            sc_array_reset (cta);
+          }
+        }
+      }
+
+      P4EST_ASSERT (btype == P4EST_CONNECT_FULL);
+    }
+  }
+  P4EST_ASSERT (local_num == p4est->local_num_quadrants);
+  for (nt = SC_MAX (p4est->last_local_tree + 1, 0); nt <= num_trees; ++nt) {
+    /* needs to cover all trees if this processor is empty */
+    /* needs to run inclusive on num_trees */
+    gl->mirror_tree_offsets[nt] = (p4est_locidx_t) gl->mirrors.elem_count;
+  }
+
+failtest:
+  if (tol == P4EST_GHOST_UNBALANCED_FAIL) {
+    if (p4est_comm_sync_flag (p4est, failed, MPI_BOR)) {
+      p4est_ghost_mirror_reset (gl, &m, 0);
+
+      for (i = 0; i < num_procs; ++i) {
+        buf = p4est_ghost_array_index (&send_bufs, i);
+        sc_array_reset (buf);
+      }
+      sc_array_reset (&send_bufs);
+
+      for (i = 0; i < P4EST_DIM - 1; ++i) {
+        sc_array_reset (&procs[i]);
+      }
+
+      p4est_ghost_destroy (gl);
+
+      return NULL;
+    }
+  }
+  else if (tol == P4EST_GHOST_UNBALANCED_ABORT) {
+    SC_CHECK_ABORT (!failed, "Ghost layer");
+  }
+
+  /* Count the number of peers that I send to and receive from */
+  for (i = 0, num_peers = 0; i < num_procs; ++i) {
+    buf = p4est_ghost_array_index (&send_bufs, i);
+    if (buf->elem_count > 0)
+      ++num_peers;
+  }
+
+  recv_request = P4EST_ALLOC (MPI_Request, 2 * num_peers);
+  send_request = P4EST_ALLOC (MPI_Request, 2 * num_peers);
+
+  recv_counts = P4EST_ALLOC (p4est_locidx_t, 2 * num_peers);
+  send_counts = recv_counts + num_peers;
+
+  recv_load_request = recv_request + num_peers;
+  send_load_request = send_request + num_peers;
+
+  /* Post receives for the counts of ghosts to be received */
+  for (i = 0, peer = 0; i < num_procs; ++i) {
+    buf = p4est_ghost_array_index (&send_bufs, i);
+    if (buf->elem_count > 0) {
+      peer_proc = i;
+      P4EST_ASSERT (peer_proc != rank);
+      P4EST_LDEBUGF ("ghost layer post count receive from %d\n", peer_proc);
+      mpiret = MPI_Irecv (recv_counts + peer, 1, P4EST_MPI_LOCIDX,
+                          peer_proc, P4EST_COMM_GHOST_COUNT, comm,
+                          recv_request + peer);
+      SC_CHECK_MPI (mpiret);
+      ++peer;
+    }
+  }
+
+  /* Send the counts of ghosts that are going to be sent */
+  for (i = 0, peer = 0; i < num_procs; ++i) {
+    buf = p4est_ghost_array_index (&send_bufs, i);
+    if (buf->elem_count > 0) {
+      peer_proc = i;
+      send_counts[peer] = (p4est_locidx_t) buf->elem_count;
+      P4EST_LDEBUGF ("ghost layer post count send %lld to %d\n",
+                     (long long) send_counts[peer], peer_proc);
+      mpiret = MPI_Isend (send_counts + peer, 1, P4EST_MPI_LOCIDX,
+                          peer_proc, P4EST_COMM_GHOST_COUNT,
+                          comm, send_request + peer);
+      SC_CHECK_MPI (mpiret);
+      ++peer;
+    }
+  }
+
+  /* The mirrors can be assembled here since they are defined on the sender */
+  p4est_ghost_mirror_reset (gl, &m, 1);
+
+  /* Wait for the counts */
+  if (num_peers > 0) {
+    mpiret = MPI_Waitall (num_peers, recv_request, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+
+    mpiret = MPI_Waitall (num_peers, send_request, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (i = 0; i < num_peers; ++i) {
+    P4EST_ASSERT (recv_request[i] == MPI_REQUEST_NULL);
+  }
+  for (i = 0; i < num_peers; ++i) {
+    P4EST_ASSERT (send_request[i] == MPI_REQUEST_NULL);
+  }
+#endif
+
+  /* Count ghosts */
+  for (peer = 0, num_ghosts = 0; peer < num_peers; ++peer) {
+    P4EST_ASSERT (recv_counts[peer] > 0);
+    num_ghosts += recv_counts[peer];    /* same type */
+  }
+  P4EST_VERBOSEF ("Total quadrants skipped %lld ghosts to receive %lld\n",
+                  (long long) skipped, (long long) num_ghosts);
+
+  /* Allocate space for the ghosts */
+  sc_array_resize (ghost_layer, (size_t) num_ghosts);
+
+  /* Post receives for the ghosts */
+  for (i = 0, peer = 0, ghost_offset = 0; i < num_procs; ++i) {
+    buf = p4est_ghost_array_index (&send_bufs, i);
+    if (buf->elem_count > 0) {
+      peer_proc = i;
+      P4EST_LDEBUGF
+        ("ghost layer post ghost receive %lld quadrants from %d\n",
+         (long long) recv_counts[peer], peer_proc);
+      mpiret =
+        MPI_Irecv (ghost_layer->array +
+                   ghost_offset * sizeof (p4est_quadrant_t),
+                   (int) (recv_counts[peer] * sizeof (p4est_quadrant_t)),
+                   MPI_BYTE, peer_proc, P4EST_COMM_GHOST_LOAD, comm,
+                   recv_load_request + peer);
+      SC_CHECK_MPI (mpiret);
+
+      ghost_offset += recv_counts[peer];        /* same type */
+      ++peer;
+    }
+    /* proc_offsets[0] is set at beginning of this function */
+    gl->proc_offsets[i + 1] = ghost_offset;
+  }
+  P4EST_ASSERT (ghost_offset == num_ghosts);
+
+  /* Send the ghosts */
+  for (i = 0, peer = 0; i < num_procs; ++i) {
+    buf = p4est_ghost_array_index (&send_bufs, i);
+    if (buf->elem_count > 0) {
+      peer_proc = i;
+      P4EST_ASSERT ((p4est_locidx_t) buf->elem_count == send_counts[peer]);
+      P4EST_LDEBUGF ("ghost layer post ghost send %lld quadrants to %d\n",
+                     (long long) send_counts[peer], peer_proc);
+      mpiret =
+        MPI_Isend (buf->array,
+                   (int) (send_counts[peer] * sizeof (p4est_quadrant_t)),
+                   MPI_BYTE, peer_proc, P4EST_COMM_GHOST_LOAD, comm,
+                   send_load_request + peer);
+      SC_CHECK_MPI (mpiret);
+      ++peer;
+    }
+  }
+
+  /* Wait for everything */
+  if (num_peers > 0) {
+    mpiret = MPI_Waitall (num_peers, recv_load_request, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+
+    mpiret = MPI_Waitall (num_peers, send_load_request, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+
+  /* Clean up */
+  P4EST_FREE (recv_counts);
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (i = 0; i < num_peers; ++i) {
+    P4EST_ASSERT (recv_load_request[i] == MPI_REQUEST_NULL);
+  }
+  for (i = 0; i < num_peers; ++i) {
+    P4EST_ASSERT (send_load_request[i] == MPI_REQUEST_NULL);
+  }
+  q2 = NULL;
+  for (li = 0; li < num_ghosts; ++li) {
+    q = p4est_quadrant_array_index (ghost_layer, (size_t) li);
+    P4EST_ASSERT (p4est_quadrant_is_valid (q));
+    P4EST_ASSERT (q->p.piggy1.which_tree >= 0 &&
+                  q->p.piggy1.which_tree < num_trees);
+    P4EST_ASSERT (q->p.piggy3.local_num >= 0);
+    if (q2 != NULL) {
+      P4EST_ASSERT (p4est_quadrant_compare_piggy (q2, q) < 0);
+    }
+    q2 = q;
+  }
+#endif
+
+  P4EST_FREE (recv_request);
+  P4EST_FREE (send_request);
+
+  for (i = 0; i < num_procs; ++i) {
+    buf = p4est_ghost_array_index (&send_bufs, i);
+    sc_array_reset (buf);
+  }
+  sc_array_reset (&send_bufs);
+  for (i = 0; i < P4EST_DIM - 1; ++i) {
+    sc_array_reset (&procs[i]);
+  }
+#endif /* P4EST_ENABLE_MPI */
+
+  /* calculate tree offsets */
+  sc_array_init (&split, sizeof (size_t));
+  sc_array_split (ghost_layer, &split,
+                  (size_t) num_trees, ghost_tree_type, NULL);
+  P4EST_ASSERT (split.elem_count == (size_t) num_trees + 1);
+  for (nt = 0; nt <= num_trees; ++nt) {
+    ppz = (size_t *) sc_array_index (&split, (size_t) nt);
+    gl->tree_offsets[nt] = *ppz;
+#ifdef P4EST_ENABLE_DEBUG
+    if (nt > 0) {
+      p4est_locidx_t      lk;
+      p4est_quadrant_t   *q3;
+
+      for (lk = gl->tree_offsets[nt - 1]; lk < gl->tree_offsets[nt]; ++lk) {
+        q3 = p4est_quadrant_array_index (ghost_layer, (size_t) lk);
+        SC_CHECK_ABORT (q3->p.which_tree == nt - 1, "Ghost tree offset");
+      }
+    }
+#endif
+#ifndef P4EST_ENABLE_MPI
+    gl->mirror_tree_offsets[nt] = 0;
+#endif
+  }
+  sc_array_reset (&split);
+  P4EST_ASSERT (gl->tree_offsets[0] == 0);
+  P4EST_ASSERT (gl->proc_offsets[0] == 0);
+
+  gl->mirror_proc_fronts = gl->mirror_proc_mirrors;
+  gl->mirror_proc_front_offsets = gl->mirror_proc_offsets;
+
+  P4EST_ASSERT (p4est_ghost_is_valid (p4est, gl));
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_ghost_new\n");
+  return gl;
+}
+
+p4est_ghost_t      *
+p4est_ghost_new (p4est_t * p4est, p4est_connect_type_t btype)
+{
+  return p4est_ghost_new_check (p4est, btype, P4EST_GHOST_UNBALANCED_ALLOW);
+}
+
+void
+p4est_ghost_destroy (p4est_ghost_t * ghost)
+{
+  sc_array_reset (&ghost->ghosts);
+  P4EST_FREE (ghost->tree_offsets);
+  P4EST_FREE (ghost->proc_offsets);
+
+  if (ghost->mirror_proc_fronts != ghost->mirror_proc_mirrors) {
+    P4EST_ASSERT (ghost->mirror_proc_front_offsets !=
+                  ghost->mirror_proc_offsets);
+    P4EST_FREE (ghost->mirror_proc_fronts);
+    P4EST_FREE (ghost->mirror_proc_front_offsets);
+  }
+
+  sc_array_reset (&ghost->mirrors);
+  P4EST_FREE (ghost->mirror_tree_offsets);
+  P4EST_FREE (ghost->mirror_proc_mirrors);
+  P4EST_FREE (ghost->mirror_proc_offsets);
+
+  P4EST_FREE (ghost);
+}
+
+unsigned
+p4est_ghost_checksum (p4est_t * p4est, p4est_ghost_t * ghost)
+{
+  unsigned            crc;
+  uint32_t           *check;
+  size_t              zz, csize, qcount, offset;
+  size_t              nt1, np1, local_count;
+  sc_array_t         *quadrants, *checkarray;
+  p4est_quadrant_t   *q;
+
+  quadrants = &ghost->ghosts;
+  qcount = quadrants->elem_count;
+  nt1 = (size_t) p4est->connectivity->num_trees + 1;
+  np1 = (size_t) p4est->mpisize + 1;
+
+  P4EST_ASSERT (quadrants->elem_size == sizeof (p4est_quadrant_t));
+
+  csize = sizeof (uint32_t);
+  checkarray = sc_array_new (csize);
+
+  local_count = qcount * (P4EST_DIM + 3) + nt1 + np1;
+  sc_array_resize (checkarray, local_count);
+
+  /* checksum ghost quadrants */
+  for (zz = 0; zz < qcount; ++zz) {
+    q = p4est_quadrant_array_index (quadrants, zz);
+    P4EST_ASSERT (p4est_quadrant_is_valid (q));
+    check = (uint32_t *) sc_array_index (checkarray, zz * (P4EST_DIM + 3));
+    check[0] = htonl ((uint32_t) q->x);
+    check[1] = htonl ((uint32_t) q->y);
+#ifdef P4_TO_P8
+    check[2] = htonl ((uint32_t) q->z);
+#endif
+    check[P4EST_DIM] = htonl ((uint32_t) q->level);
+    check[P4EST_DIM + 1] = htonl ((uint32_t) q->p.piggy3.which_tree);
+    check[P4EST_DIM + 2] = htonl ((uint32_t) q->p.piggy3.local_num);
+  }
+
+  /* checksum tree_offsets */
+  offset = qcount * (P4EST_DIM + 3);
+  for (zz = 0; zz < nt1; ++zz) {
+    check = (uint32_t *) sc_array_index (checkarray, offset + zz);
+    *check = htonl ((uint32_t) ghost->tree_offsets[zz]);
+  }
+
+  /* checksum proc_offsets */
+  offset += nt1;
+  for (zz = 0; zz < np1; ++zz) {
+    check = (uint32_t *) sc_array_index (checkarray, offset + zz);
+    *check = htonl ((uint32_t) ghost->proc_offsets[zz]);
+  }
+  P4EST_ASSERT (offset + zz == local_count);
+
+  /* compute parallel checksum */
+  crc = sc_array_checksum (checkarray);
+  sc_array_destroy (checkarray);
+
+  return p4est_comm_checksum (p4est, crc, csize * local_count);
+}
+
+void
+p4est_ghost_exchange_data (p4est_t * p4est, p4est_ghost_t * ghost,
+                           void *ghost_data)
+{
+  size_t              zz;
+  size_t              data_size;
+#ifdef P4EST_ENABLE_DEBUG
+  p4est_topidx_t      prev_tree;
+#endif
+  p4est_topidx_t      which_tree;
+  p4est_locidx_t      which_quad;
+  p4est_quadrant_t   *mirror, *q;
+  p4est_tree_t       *tree;
+  void              **mirror_data;
+
+  mirror_data = P4EST_ALLOC (void *, ghost->mirrors.elem_count);
+
+  data_size = p4est->data_size == 0 ? sizeof (void *) : p4est->data_size;
+#ifdef P4EST_ENABLE_DEBUG
+  prev_tree = -1;
+#endif
+  for (zz = 0; zz < ghost->mirrors.elem_count; ++zz) {
+    mirror = p4est_quadrant_array_index (&ghost->mirrors, zz);
+    which_tree = mirror->p.piggy3.which_tree;
+    P4EST_ASSERT (p4est->first_local_tree <= which_tree &&
+                  which_tree <= p4est->last_local_tree);
+    P4EST_ASSERT (prev_tree <= which_tree);
+#ifdef P4EST_ENABLE_DEBUG
+    prev_tree = which_tree;
+#endif
+    tree = p4est_tree_array_index (p4est->trees, which_tree);
+    which_quad = mirror->p.piggy3.local_num - tree->quadrants_offset;
+    P4EST_ASSERT (0 <= which_quad &&
+                  which_quad < (p4est_locidx_t) tree->quadrants.elem_count);
+    q = p4est_quadrant_array_index (&tree->quadrants, which_quad);
+    mirror_data[zz] =
+      p4est->data_size == 0 ? &q->p.user_data : q->p.user_data;
+  }
+
+  p4est_ghost_exchange_custom (p4est, ghost, data_size,
+                               mirror_data, ghost_data);
+  P4EST_FREE (mirror_data);
+}
+
+void
+p4est_ghost_exchange_custom (p4est_t * p4est, p4est_ghost_t * ghost,
+                             size_t data_size,
+                             void **mirror_data, void *ghost_data)
+{
+  const int           num_procs = p4est->mpisize;
+  int                 mpiret;
+  int                 q;
+  char               *mem, **sbuf;
+  size_t              zz;
+  sc_array_t          requests, sbuffers;
+  p4est_locidx_t      ng_excl, ng_incl, ng, theg;
+  p4est_locidx_t      mirr;
+  sc_MPI_Request     *r;
+
+  if (data_size == 0) {
+    return;
+  }
+  sc_array_init (&requests, sizeof (sc_MPI_Request));
+  sc_array_init (&sbuffers, sizeof (char *));
+
+  /* receive data from other processors */
+  ng_excl = 0;
+  for (q = 0; q < num_procs; ++q) {
+    ng_incl = ghost->proc_offsets[q + 1];
+    ng = ng_incl - ng_excl;
+    P4EST_ASSERT (ng >= 0);
+    if (ng > 0) {
+      r = (sc_MPI_Request *) sc_array_push (&requests);
+      mpiret = sc_MPI_Irecv ((char *) ghost_data + ng_excl * data_size,
+                             ng * data_size, sc_MPI_BYTE, q,
+                             P4EST_COMM_GHOST_EXCHANGE, p4est->mpicomm, r);
+      SC_CHECK_MPI (mpiret);
+      ng_excl = ng_incl;
+    }
+  }
+  P4EST_ASSERT (ng_excl == (p4est_locidx_t) ghost->ghosts.elem_count);
+
+  /* send data to other processors */
+  ng_excl = 0;
+  for (q = 0; q < num_procs; ++q) {
+    ng_incl = ghost->mirror_proc_offsets[q + 1];
+    ng = ng_incl - ng_excl;
+    P4EST_ASSERT (ng >= 0);
+    if (ng > 0) {
+      /* every peer populates its own send buffer */
+      sbuf = (char **) sc_array_push (&sbuffers);
+      mem = *sbuf = P4EST_ALLOC (char, ng * data_size);
+      for (theg = 0; theg < ng; ++theg) {
+        mirr = ghost->mirror_proc_mirrors[ng_excl + theg];
+        P4EST_ASSERT (0 <= mirr && (size_t) mirr < ghost->mirrors.elem_count);
+        memcpy (mem, mirror_data[mirr], data_size);
+        mem += data_size;
+      }
+      r = (sc_MPI_Request *) sc_array_push (&requests);
+      mpiret = sc_MPI_Isend (*sbuf, ng * data_size, sc_MPI_BYTE, q,
+                             P4EST_COMM_GHOST_EXCHANGE, p4est->mpicomm, r);
+      SC_CHECK_MPI (mpiret);
+      ng_excl = ng_incl;
+    }
+  }
+
+  /* wait and clean up */
+  mpiret = sc_MPI_Waitall (requests.elem_count, (sc_MPI_Request *)
+                           requests.array, sc_MPI_STATUSES_IGNORE);
+  SC_CHECK_MPI (mpiret);
+  sc_array_reset (&requests);
+  for (zz = 0; zz < sbuffers.elem_count; ++zz) {
+    sbuf = (char **) sc_array_index (&sbuffers, zz);
+    P4EST_FREE (*sbuf);
+  }
+  sc_array_reset (&sbuffers);
+}
+
+void
+p4est_ghost_exchange_custom_levels (p4est_t * p4est, p4est_ghost_t * ghost,
+                                    int minlevel, int maxlevel,
+                                    size_t data_size,
+                                    void **mirror_data, void *ghost_data)
+{
+  const int           num_procs = p4est->mpisize;
+  int                 mpiret;
+  int                 q;
+  int                 i, expected, remaining, received, *peers;
+  int                *theq, *qactive, *qbuffer;
+  char               *mem, **rbuf, **sbuf;
+  size_t              zz;
+  sc_array_t          rrequests, srequests, rbuffers, sbuffers;
+  p4est_locidx_t      ng_excl, ng_incl, ng, theg;
+  p4est_locidx_t      lmatches;
+  p4est_locidx_t      mirr;
+  p4est_quadrant_t   *g, *m;
+  sc_MPI_Request     *r;
+
+  if (minlevel <= 0 && maxlevel >= P4EST_QMAXLEVEL) {
+    /* this saves a copy for the ghost quadrants */
+    p4est_ghost_exchange_custom (p4est, ghost,
+                                 data_size, mirror_data, ghost_data);
+    return;
+  }
+  if (data_size == 0 || minlevel > maxlevel) {
+    /* nothing to do */
+    return;
+  }
+
+  sc_array_init (&rrequests, sizeof (sc_MPI_Request));
+  sc_array_init (&srequests, sizeof (sc_MPI_Request));
+  sc_array_init (&rbuffers, sizeof (char *));
+  sc_array_init (&sbuffers, sizeof (char *));
+  qactive = P4EST_ALLOC (int, num_procs);
+  qbuffer = P4EST_ALLOC (int, num_procs);
+
+  /* receive data from other processors */
+  ng_excl = 0;
+  for (q = 0; q < num_procs; ++q) {
+    qactive[q] = -1;
+    qbuffer[q] = -1;
+    ng_incl = ghost->proc_offsets[q + 1];
+    ng = ng_incl - ng_excl;
+    P4EST_ASSERT (ng >= 0);
+    if (ng > 0) {
+      P4EST_ASSERT (q != p4est->mpirank);
+      /* run through ghosts to count the matching level quadrants */
+      for (lmatches = 0, theg = 0; theg < ng; ++theg) {
+        g = p4est_quadrant_array_index (&ghost->ghosts, ng_excl + theg);
+        if (minlevel <= (int) g->level && (int) g->level <= maxlevel) {
+          ++lmatches;
+        }
+      }
+      if (lmatches > 0) {
+        theq = qactive + rrequests.elem_count;
+        r = (sc_MPI_Request *) sc_array_push (&rrequests);
+        if (lmatches < ng) {
+          /* every peer populates its own receive buffer */
+          *theq = q;
+          qbuffer[q] = (int) rbuffers.elem_count;
+          rbuf = (char **) sc_array_push (&rbuffers);
+          *rbuf = P4EST_ALLOC (char, lmatches * data_size);
+          mpiret = sc_MPI_Irecv (*rbuf, lmatches * data_size, sc_MPI_BYTE, q,
+                                 P4EST_COMM_GHOST_EXCHANGE, p4est->mpicomm,
+                                 r);
+        }
+        else {
+          /* use the ghost data memory as is */
+          *theq = -1;
+          mpiret = sc_MPI_Irecv ((char *) ghost_data + ng_excl * data_size,
+                                 ng * data_size, sc_MPI_BYTE, q,
+                                 P4EST_COMM_GHOST_EXCHANGE, p4est->mpicomm,
+                                 r);
+        }
+        SC_CHECK_MPI (mpiret);
+      }
+      ng_excl = ng_incl;
+    }
+  }
+  P4EST_ASSERT (ng_excl == (p4est_locidx_t) ghost->ghosts.elem_count);
+
+  /* send data to other processors */
+  ng_excl = 0;
+  for (q = 0; q < num_procs; ++q) {
+    ng_incl = ghost->mirror_proc_offsets[q + 1];
+    ng = ng_incl - ng_excl;
+    P4EST_ASSERT (ng >= 0);
+    if (ng > 0) {
+      P4EST_ASSERT (q != p4est->mpirank);
+      /* run through mirrors to count the matching level quadrants */
+      for (lmatches = 0, theg = 0; theg < ng; ++theg) {
+        mirr = ghost->mirror_proc_mirrors[ng_excl + theg];
+        m = p4est_quadrant_array_index (&ghost->mirrors, mirr);
+        if (minlevel <= (int) m->level && (int) m->level <= maxlevel) {
+          ++lmatches;
+        }
+      }
+      if (lmatches > 0) {
+        /* every peer populates its own send buffer */
+        sbuf = (char **) sc_array_push (&sbuffers);
+        mem = *sbuf = P4EST_ALLOC (char, lmatches * data_size);
+        for (theg = 0; theg < ng; ++theg) {
+          mirr = ghost->mirror_proc_mirrors[ng_excl + theg];
+          m = p4est_quadrant_array_index (&ghost->mirrors, mirr);
+          if (minlevel <= (int) m->level && (int) m->level <= maxlevel) {
+            memcpy (mem, mirror_data[mirr], data_size);
+            mem += data_size;
+          }
+        }
+        r = (sc_MPI_Request *) sc_array_push (&srequests);
+        mpiret = sc_MPI_Isend (*sbuf, lmatches * data_size, sc_MPI_BYTE, q,
+                               P4EST_COMM_GHOST_EXCHANGE, p4est->mpicomm, r);
+        SC_CHECK_MPI (mpiret);
+      }
+      ng_excl = ng_incl;
+    }
+  }
+
+  /* wait for receives and copy data into the proper result array */
+  peers = P4EST_ALLOC (int, rrequests.elem_count);
+  expected = remaining = (int) rrequests.elem_count;
+  while (remaining > 0) {
+    mpiret = sc_MPI_Waitsome (expected, (sc_MPI_Request *) rrequests.array,
+                              &received, peers, sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+    P4EST_ASSERT (received != sc_MPI_UNDEFINED);
+    P4EST_ASSERT (received > 0);
+    for (i = 0; i < received; ++i) {
+      P4EST_ASSERT (0 <= peers[i] && peers[i] < (int) rrequests.elem_count);
+      q = qactive[peers[i]];
+      if (q >= 0) {
+        P4EST_ASSERT (q != p4est->mpirank && q < num_procs);
+        ng_excl = ghost->proc_offsets[q];
+        ng_incl = ghost->proc_offsets[q + 1];
+        ng = ng_incl - ng_excl;
+        P4EST_ASSERT (ng > 0);
+        /* run through ghosts to copy the matching level quadrants' data */
+        rbuf = (char **) sc_array_index_int (&rbuffers, qbuffer[q]);
+        for (lmatches = 0, theg = 0; theg < ng; ++theg) {
+          g = p4est_quadrant_array_index (&ghost->ghosts, ng_excl + theg);
+          if (minlevel <= (int) g->level && (int) g->level <= maxlevel) {
+            memcpy ((char *) ghost_data + (ng_excl + theg) * data_size,
+                    *rbuf + lmatches * data_size, data_size);
+            ++lmatches;
+          }
+        }
+        P4EST_FREE (*rbuf);
+        qactive[peers[i]] = -1;
+        qbuffer[q] = -1;
+      }
+    }
+    remaining -= received;
+  }
+  P4EST_FREE (peers);
+  P4EST_FREE (qactive);
+  P4EST_FREE (qbuffer);
+  sc_array_reset (&rrequests);
+  sc_array_reset (&rbuffers);
+
+  /* wait for sends and clean up */
+  mpiret = sc_MPI_Waitall (srequests.elem_count, (sc_MPI_Request *)
+                           srequests.array, sc_MPI_STATUSES_IGNORE);
+  SC_CHECK_MPI (mpiret);
+  sc_array_reset (&srequests);
+  for (zz = 0; zz < sbuffers.elem_count; ++zz) {
+    sbuf = (char **) sc_array_index (&sbuffers, zz);
+    P4EST_FREE (*sbuf);
+  }
+  sc_array_reset (&sbuffers);
+}
+
+#ifdef P4EST_ENABLE_MPI
+
+static void
+p4est_ghost_expand_insert (p4est_quadrant_t * q, p4est_topidx_t t,
+                           p4est_locidx_t idx, sc_array_t * send_bufs,
+                           int target, int owner, int is_ghost)
+{
+  sc_array_t         *send_buf =
+    (sc_array_t *) sc_array_index_int (send_bufs, target);
+  p4est_quadrant_t   *qp;
+  /* add to mirrors */
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+  qp = (p4est_quadrant_t *) sc_array_push (send_buf);
+  P4EST_QUADRANT_INIT (qp);
+  qp->x = q->x;
+  qp->y = q->y;
+#ifdef P4_TO_P8
+  qp->z = q->z;
+#endif
+  qp->level = q->level;
+  qp->p.piggy3.which_tree = t;
+  if (is_ghost) {
+    sc_array_t         *from_buf =
+      (sc_array_t *) sc_array_index_int (send_bufs, owner);
+    p4est_quadrant_t   *qp2;
+
+    P4EST_ASSERT (owner != target);
+    P4EST_ASSERT (q->p.piggy3.which_tree == t);
+    qp->p.piggy3.local_num = q->p.piggy3.local_num;
+
+    qp2 = (p4est_quadrant_t *) sc_array_push (from_buf);
+    qp2->x = q->x;
+    qp2->y = q->y;
+#ifdef P4_TO_P8
+    qp2->z = q->z;
+#endif
+    qp2->level = q->level;
+    qp2->p.piggy1.which_tree = t;
+    qp2->p.piggy1.owner_rank = target;  /* yes, we're putting the target in the
+                                           owner field */
+    P4EST_ASSERT (p4est_quadrant_is_valid (qp2));
+  }
+  else {
+    qp->p.piggy3.local_num = idx;
+  }
+  P4EST_ASSERT (p4est_quadrant_is_valid (qp));
+}
+
+static void
+p4est_ghost_expand_kernel (p4est_topidx_t t, p4est_quadrant_t * mq,
+                           p4est_topidx_t nt, p4est_quadrant_t * nq,
+                           sc_array_t * quads, int quads_is_ghost,
+                           sc_array_t * mview, sc_array_t * pview,
+                           p4est_connect_type_t btype, int point,
+                           sc_array_t * tempquads, sc_array_t * temptrees,
+                           p4est_connectivity_t * conn,
+                           p4est_locidx_t pview_offset,
+                           p4est_locidx_t quads_offset,
+                           p4est_t * p4est, int target,
+                           sc_array_t * send_bufs)
+{
+  ssize_t             fidx, lidx;
+  size_t              zz, zy;
+  int                 owner = -1;
+
+  /* we want a list of all quadrants in quads that overlap nq */
+
+  /* this will return a quad that overlaps nq, but not necessarily the first
+   * */
+  fidx = sc_array_bsearch (quads, nq, p4est_quadrant_disjoint);
+  if (fidx < 0) {
+    /* nothing to be done */
+    return;
+  }
+
+  lidx = fidx;
+
+  /* walk fidx back to find the first quad that overlaps nq */
+  while (fidx > 0) {
+    p4est_quadrant_t   *testq = p4est_quadrant_array_index (quads, (size_t)
+                                                            fidx - 1);
+
+    if (p4est_quadrant_disjoint (testq, nq)) {
+      P4EST_ASSERT (p4est_quadrant_compare (testq, nq) < 0);
+      break;
+    }
+    fidx--;
+  }
+
+  /* walk lidx forward to find the last quad that overlaps nq */
+  while (lidx < (ssize_t) quads->elem_count - 1) {
+    p4est_quadrant_t   *testq = p4est_quadrant_array_index (quads, (size_t)
+                                                            lidx + 1);
+
+    if (p4est_quadrant_disjoint (testq, nq)) {
+      P4EST_ASSERT (p4est_quadrant_compare (testq, nq) > 0);
+      break;
+    }
+    lidx++;
+  }
+
+  /* for every overlapping quadrant, test to see if mq overlaps the neighbor
+   * */
+  for (zz = (size_t) fidx; zz <= (size_t) lidx; zz++) {
+    ssize_t             midx;
+    p4est_topidx_t      nnt;
+    p4est_quadrant_t   *p = p4est_quadrant_array_index (quads, zz);
+    p4est_quadrant_t    np;
+
+    P4EST_ASSERT (p4est_quadrant_overlaps (p, nq));
+    /* if we are searching local quads, avoid trying to send mirrors back to
+     * the procs for which they are already ghosts */
+    if (!quads_is_ghost) {
+      midx = sc_array_bsearch (mview, p, p4est_quadrant_compare);
+
+      /* p is a mirror, but is it a mirror for this proc?  search in pview */
+      if (midx >= 0) {
+        ssize_t             pidx;
+        p4est_locidx_t      lmidx;
+
+        lmidx = midx + pview_offset;
+        pidx = sc_array_bsearch (pview, &lmidx, p4est_locidx_compare);
+        if (pidx >= 0) {
+          /* this is already a mirror that this proc knows about */
+          continue;
+        }
+      }
+    }
+    else {
+      /* if we are searching for ghost quads, don't send back one owned by the
+       * target proc */
+      owner = p4est_comm_find_owner (p4est, nt, p, target);
+      if (owner == target) {
+        continue;
+      }
+    }
+
+    /* now create the approriate neighbor and test for overlaps */
+    if (btype == P4EST_CONNECT_FACE) {
+      nnt = p4est_quadrant_face_neighbor_extra (p, nt, point, &np, NULL,
+                                                conn);
+      P4EST_ASSERT (nnt == nt || nnt == t);
+      if (nnt == t && p4est_quadrant_overlaps (mq, &np)) {
+        p4est_ghost_expand_insert (p, nt, (p4est_locidx_t) zz +
+                                   quads_offset, send_bufs, target, owner,
+                                   quads_is_ghost);
+      }
+    }
+#ifdef P4_TO_P8
+    else if (btype == P8EST_CONNECT_EDGE) {
+      p8est_quadrant_edge_neighbor_extra (p, nt, point, tempquads, temptrees,
+                                          NULL, conn);
+
+      for (zy = 0; zy < tempquads->elem_count; zy++) {
+        p4est_topidx_t      nnt =
+          *((p4est_topidx_t *) sc_array_index (temptrees, zy));
+
+        if (nnt == t) {
+          p4est_quadrant_t   *tempq =
+            p4est_quadrant_array_index (tempquads, zy);
+
+          if (p4est_quadrant_overlaps (mq, tempq)) {
+            p4est_ghost_expand_insert (p, nt, (p4est_locidx_t) zz +
+                                       quads_offset, send_bufs, target, owner,
+                                       quads_is_ghost);
+            break;
+          }
+        }
+      }
+      sc_array_reset (tempquads);
+      sc_array_reset (temptrees);
+    }
+#endif
+    else {
+      P4EST_ASSERT (btype == P4EST_CONNECT_CORNER);
+
+      p4est_quadrant_corner_neighbor_extra (p, nt, point, tempquads,
+                                            temptrees, NULL, conn);
+
+      for (zy = 0; zy < tempquads->elem_count; zy++) {
+        p4est_topidx_t      nnt =
+          *((p4est_topidx_t *) sc_array_index (temptrees, zy));
+
+        if (nnt == t) {
+          p4est_quadrant_t   *tempq =
+            p4est_quadrant_array_index (tempquads, zy);
+
+          if (p4est_quadrant_overlaps (mq, tempq)) {
+            /* add to mirrors */
+            p4est_ghost_expand_insert (p, nt, (p4est_locidx_t) zz +
+                                       quads_offset, send_bufs, target, owner,
+                                       quads_is_ghost);
+            break;
+          }
+        }
+      }
+      sc_array_reset (tempquads);
+      sc_array_reset (temptrees);
+    }
+  }
+}
+
+static void
+p4est_ghost_expand_int (p4est_topidx_t t, p4est_quadrant_t * mq,
+                        p4est_topidx_t nt, p4est_quadrant_t * nq,
+                        sc_array_t * pview, p4est_connect_type_t btype,
+                        int point, sc_array_t * tempquads,
+                        sc_array_t * temptrees, int target,
+                        p4est_t * p4est, p4est_ghost_t * ghost,
+                        sc_array_t * send_bufs)
+{
+  sc_array_t          mview;
+  sc_array_t          gview;
+
+  sc_array_init_view (&mview, &ghost->mirrors, ghost->mirror_tree_offsets[nt],
+                      ghost->mirror_tree_offsets[nt + 1] -
+                      ghost->mirror_tree_offsets[nt]);
+  sc_array_init_view (&gview, &ghost->ghosts, ghost->tree_offsets[nt],
+                      ghost->tree_offsets[nt + 1] - ghost->tree_offsets[nt]);
+
+  if (nt >= p4est->first_local_tree && nt <= p4est->last_local_tree) {
+    p4est_tree_t       *ntree = p4est_tree_array_index (p4est->trees, nt);
+
+    p4est_ghost_expand_kernel (t, mq, nt, nq, &ntree->quadrants, 0, &mview,
+                               pview, btype, point, tempquads, temptrees,
+                               p4est->connectivity,
+                               ghost->mirror_tree_offsets[nt],
+                               ntree->quadrants_offset,
+                               p4est, target, send_bufs);
+  }
+
+  p4est_ghost_expand_kernel (t, mq, nt, nq, &gview, 1, &mview, pview, btype,
+                             point, tempquads, temptrees, p4est->connectivity,
+                             ghost->mirror_tree_offsets[nt],
+                             ghost->tree_offsets[nt],
+                             p4est, target, send_bufs);
+
+  sc_array_reset (&mview);
+  sc_array_reset (&gview);
+}
+
+static int
+p4est_quadrant_compare_piggy_proc (const void *a, const void *b)
+{
+  const p4est_quadrant_t *A = (const p4est_quadrant_t *) a;
+  const p4est_quadrant_t *B = (const p4est_quadrant_t *) b;
+  int                 ret = p4est_quadrant_compare_piggy (a, b);
+
+  if (ret) {
+    return ret;
+  }
+  return (A->p.piggy1.owner_rank - B->p.piggy1.owner_rank);
+}
+
+#endif /* P4EST_ENABLE_MPI */
+
+static void
+p4est_ghost_expand_internal (p4est_t * p4est, p4est_lnodes_t * lnodes,
+                             p4est_ghost_t * ghost)
+{
+#ifdef P4EST_ENABLE_MPI
+  int                 p;
+  int                 mpisize = p4est->mpisize;
+  int                 mpirank = p4est->mpirank;
+  MPI_Comm            comm = p4est->mpicomm;
+  p4est_connect_type_t btype = ghost->btype;
+  sc_array_t         *mirrors = &ghost->mirrors;
+  sc_array_t         *new_mirrors;
+  p4est_locidx_t     *mirror_tree_offsets = ghost->mirror_tree_offsets;
+  p4est_locidx_t     *mirror_proc_mirrors = ghost->mirror_proc_mirrors;
+  p4est_locidx_t     *mirror_proc_offsets = ghost->mirror_proc_offsets;
+  p4est_locidx_t     *proc_offsets = ghost->proc_offsets;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  p4est_locidx_t     *mpf, *mpfo;
+  p4est_locidx_t     *send_counts, *recv_counts;
+  MPI_Request        *recv_request, *send_request;
+  MPI_Request        *recv_load_request, *send_load_request;
+  int                 num_peers;
+  int                 peer;
+  int                 mpiret;
+  sc_array_t         *send_bufs, *buf;
+  size_t              zz, *ppz;
+  p4est_topidx_t      t;
+  sc_array_t         *nmpma, *nmpfa;
+  p4est_locidx_t      old_num_ghosts, num_new_ghosts, ghost_offset;
+  p4est_locidx_t      old_num_mirrors, new_count, new_num_mirrors;
+  sc_array_t         *ghost_layer = &ghost->ghosts;
+  sc_array_t          split;
+  sc_array_t         *tempquads;
+  sc_array_t         *temptrees;
+  sc_array_t         *tempquads2;
+  sc_array_t         *temptrees2;
+  sc_array_t         *npoints;
+  p4est_locidx_t     *ntq_offset = NULL;
+  p4est_locidx_t     *node_to_quad = NULL;
+  p4est_topidx_t     *node_to_tree = NULL;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING "_ghost_expand %s\n",
+                            p4est_connect_type_string (btype));
+  p4est_log_indent_push ();
+
+  tempquads = sc_array_new (sizeof (p4est_quadrant_t));
+  temptrees = sc_array_new (sizeof (p4est_topidx_t));
+  tempquads2 = sc_array_new (sizeof (p4est_quadrant_t));
+  temptrees2 = sc_array_new (sizeof (p4est_topidx_t));
+  npoints = sc_array_new (sizeof (int));
+
+  /* if lnodes, build node_to_quad */
+  if (lnodes) {
+    p4est_gloidx_t    **mirror_data;
+    p4est_locidx_t      il, num_mirrors, qid, K = lnodes->num_local_elements;
+    p4est_locidx_t      G = (p4est_locidx_t) ghost->ghosts.elem_count;
+    p4est_locidx_t      N = lnodes->num_local_nodes;
+    int                 v, nid;
+    int                 vnodes = lnodes->vnodes;
+    int                *node_to_quad_count;
+    int                *quad_counted;
+    p4est_gloidx_t     *quad_to_node_global;
+    p4est_gloidx_t     *quad_to_node_global_ghost;
+    p4est_topidx_t      flt, llt, t;
+
+    quad_to_node_global = P4EST_ALLOC (p4est_gloidx_t, K * vnodes);
+    quad_to_node_global_ghost = P4EST_ALLOC (p4est_gloidx_t, G * vnodes);
+#ifdef P4EST_ENABLE_DEBUG
+    memset (quad_to_node_global_ghost, -1,
+            vnodes * G * sizeof (p4est_gloidx_t));
+#endif
+
+    mirror_data = P4EST_ALLOC (p4est_gloidx_t *, ghost->mirrors.elem_count);
+
+    for (qid = 0; qid < K * vnodes; qid++) {
+      quad_to_node_global[qid] =
+        p4est_lnodes_global_index (lnodes, lnodes->element_nodes[qid]);
+    }
+
+    num_mirrors = (p4est_locidx_t) ghost->mirrors.elem_count;
+    for (il = 0; (size_t) il < num_mirrors; il++) {
+      p4est_quadrant_t   *q;
+
+      q = p4est_quadrant_array_index (&ghost->mirrors, il);
+
+      qid = q->p.piggy3.local_num;
+      mirror_data[il] = &quad_to_node_global[qid * vnodes];
+    }
+
+    p4est_ghost_exchange_custom (p4est, ghost,
+                                 (size_t) vnodes * sizeof (p4est_gloidx_t),
+                                 (void **) mirror_data,
+                                 quad_to_node_global_ghost);
+    P4EST_FREE (mirror_data);
+    P4EST_FREE (quad_to_node_global);
+
+    /* convert global back to local */
+    for (qid = 0; qid < G; qid++) {
+      for (v = 0; v < vnodes; v++) {
+        p4est_gloidx_t      gnid;
+
+        gnid = quad_to_node_global_ghost[qid * vnodes + v];
+#ifdef P4EST_ENABLE_DEBUG
+        P4EST_ASSERT (gnid >= 0);
+#endif
+        nid = -1;
+        if ((gnid >= lnodes->global_offset) &&
+            (gnid < (lnodes->global_offset + lnodes->owned_count))) {
+          nid = gnid - lnodes->global_offset;
+        }
+        else {
+          sc_array_t          view;
+          ssize_t             idx;
+
+          sc_array_init_data (&view, lnodes->nonlocal_nodes,
+                              sizeof (p4est_gloidx_t),
+                              (size_t) (lnodes->num_local_nodes -
+                                        lnodes->owned_count));
+
+          idx = sc_array_bsearch (&view, &gnid, p4est_gloidx_compare);
+          if (idx >= 0) {
+            nid = idx + lnodes->owned_count;
+          }
+        }
+        P4EST_ASSERT (nid == -1
+                      || p4est_lnodes_global_index (lnodes, nid) == gnid);
+        quad_to_node_global_ghost[qid * vnodes + v] = nid;
+      }
+    }
+
+    node_to_quad_count = P4EST_ALLOC_ZERO (int, N);
+    quad_counted = P4EST_ALLOC_ZERO (int, K);
+
+    /* first count fronts */
+    for (p = 0; p < mpisize; p++) {
+      p4est_locidx_t      ilstart, ilend;
+
+      ilstart = ghost->mirror_proc_front_offsets[p];
+      ilend = ghost->mirror_proc_front_offsets[p + 1];
+
+      for (il = ilstart; il < ilend; il++) {
+        p4est_quadrant_t   *q;
+        int                 v;
+
+        qid = ghost->mirror_proc_fronts[il];
+        q = p4est_quadrant_array_index (&ghost->mirrors, (size_t) qid);
+        qid = q->p.piggy3.local_num;
+        if (!quad_counted[qid]) {
+          quad_counted[qid] = 1;
+          for (v = 0; v < vnodes; v++) {
+            nid = lnodes->element_nodes[qid * vnodes + v];
+            node_to_quad_count[nid]++;
+          }
+        }
+      }
+    }
+
+    /* count the rest of the quads and ghosts, only incrementing nodes that
+     * have a front quad adjacent */
+    for (qid = 0; qid < K; qid++) {
+      if (!quad_counted[qid]) {
+        quad_counted[qid] = 1;
+        for (v = 0; v < vnodes; v++) {
+          nid = lnodes->element_nodes[qid * vnodes + v];
+          if (node_to_quad_count[nid]) {
+            node_to_quad_count[nid]++;
+          }
+        }
+      }
+    }
+    for (qid = 0; qid < G; qid++) {
+      for (v = 0; v < vnodes; v++) {
+        nid = (p4est_locidx_t) quad_to_node_global_ghost[qid * vnodes + v];
+        if (nid >= 0) {
+          if (node_to_quad_count[nid]) {
+            node_to_quad_count[nid]++;
+          }
+        }
+      }
+    }
+    P4EST_FREE (quad_counted);
+
+    ntq_offset = P4EST_ALLOC (p4est_locidx_t, N + 1);
+    ntq_offset[0] = 0;
+    for (nid = 0; nid < N; nid++) {
+      ntq_offset[nid + 1] = ntq_offset[nid] + node_to_quad_count[nid];
+    }
+    node_to_quad = P4EST_ALLOC (p4est_locidx_t, ntq_offset[N]);
+    node_to_tree = P4EST_ALLOC (p4est_topidx_t, ntq_offset[N]);
+#ifdef P4EST_ENABLE_DEBUG
+    memset (node_to_quad, -1, ntq_offset[N] * sizeof (p4est_locidx_t));
+    memset (node_to_tree, -1, ntq_offset[N] * sizeof (p4est_topidx_t));
+#endif
+
+    memset (node_to_quad_count, 0, N * sizeof (int));
+
+    flt = p4est->first_local_tree;
+    llt = p4est->last_local_tree;
+    for (qid = 0, t = flt; t <= llt; t++) {
+      p4est_tree_t       *tree = p4est_tree_array_index (p4est->trees, t);
+      p4est_locidx_t      nquads =
+        (p4est_locidx_t) tree->quadrants.elem_count;
+
+      for (il = 0; il < nquads; il++, qid++) {
+        for (v = 0; v < vnodes; v++) {
+          p4est_locidx_t      ilstart;
+          nid = lnodes->element_nodes[qid * vnodes + v];
+          ilstart = ntq_offset[nid];
+          if (ntq_offset[nid + 1] > ilstart) {
+            node_to_quad[ilstart + node_to_quad_count[nid]] = qid;
+            node_to_tree[ilstart + node_to_quad_count[nid]++] = t;
+          }
+        }
+      }
+    }
+    for (qid = 0; qid < G; qid++) {
+      p4est_quadrant_t   *q =
+        p4est_quadrant_array_index (&ghost->ghosts, (size_t) qid);
+
+      for (v = 0; v < vnodes; v++) {
+        p4est_locidx_t      ilstart;
+        nid = (p4est_locidx_t) quad_to_node_global_ghost[qid * vnodes + v];
+        if (nid >= 0) {
+          ilstart = ntq_offset[nid];
+          if (ntq_offset[nid + 1] > ilstart) {
+            node_to_quad[ilstart + node_to_quad_count[nid]] = -(qid + 1);
+            node_to_tree[ilstart + node_to_quad_count[nid]++] =
+              q->p.piggy3.which_tree;
+          }
+        }
+      }
+    }
+#ifdef P4EST_ENABLE_DEBUG
+    for (qid = 0; qid < ntq_offset[N]; qid++) {
+      P4EST_ASSERT (node_to_tree[qid] >= 0);
+    }
+#endif
+
+    P4EST_FREE (node_to_quad_count);
+    P4EST_FREE (quad_to_node_global_ghost);
+  }
+
+  /* post recvs */
+  for (p = 0, num_peers = 0; p < mpisize; p++) {
+    if (mirror_proc_offsets[p + 1] != mirror_proc_offsets[p]) {
+      /* this is an important assertion: if any proc is part of my ghost
+       * layer, I am part of its ghost layer */
+      P4EST_ASSERT (proc_offsets[p + 1] != proc_offsets[p]);
+      num_peers++;
+    }
+  }
+  recv_request = P4EST_ALLOC (MPI_Request, 2 * num_peers);
+  send_request = P4EST_ALLOC (MPI_Request, 2 * num_peers);
+
+  recv_counts = P4EST_ALLOC (p4est_locidx_t, 2 * num_peers);
+  send_counts = recv_counts + num_peers;
+
+  recv_load_request = recv_request + num_peers;
+  send_load_request = send_request + num_peers;
+
+  send_bufs = sc_array_new_size (sizeof (sc_array_t), mpisize);
+  for (p = 0; p < mpisize; p++) {
+    buf = (sc_array_t *) sc_array_index (send_bufs, p);
+    sc_array_init (buf, sizeof (p4est_quadrant_t));
+  }
+
+  for (p = 0, peer = 0; p < mpisize; p++) {
+    if (mirror_proc_offsets[p + 1] != mirror_proc_offsets[p]) {
+      P4EST_ASSERT (p != mpirank);
+      P4EST_LDEBUGF ("ghost layer expand post count receive from %d\n", p);
+      mpiret = MPI_Irecv (recv_counts + peer, 1, P4EST_MPI_LOCIDX, p,
+                          P4EST_COMM_GHOST_EXPAND_COUNT, comm, recv_request +
+                          peer);
+      SC_CHECK_MPI (mpiret);
+      peer++;
+    }
+  }
+
+  if (ghost->mirror_proc_fronts == ghost->mirror_proc_mirrors) {
+    /* create the fronts: the last quads added to the mirrors */
+    P4EST_ASSERT (ghost->mirror_proc_front_offsets ==
+                  ghost->mirror_proc_offsets);
+
+    ghost->mirror_proc_fronts = P4EST_ALLOC (p4est_locidx_t,
+                                             mirror_proc_offsets[mpisize]);
+    memcpy (ghost->mirror_proc_fronts, mirror_proc_mirrors,
+            sizeof (p4est_locidx_t) * mirror_proc_offsets[mpisize]);
+
+    ghost->mirror_proc_front_offsets = P4EST_ALLOC (p4est_locidx_t,
+                                                    mpisize + 1);
+    memcpy (ghost->mirror_proc_front_offsets, mirror_proc_offsets,
+            sizeof (p4est_locidx_t) * (mpisize + 1));
+  }
+  mpf = ghost->mirror_proc_fronts;
+  mpfo = ghost->mirror_proc_front_offsets;
+
+  /* for every proc */
+  for (p = 0; p < mpisize; p++) {
+    /* get mirror_proc_offsets */
+    p4est_locidx_t      first_mirror = mpfo[p];
+    p4est_locidx_t      end_mirror = mpfo[p + 1];
+    size_t              zm;
+    sc_array_t          pview;
+
+    if (mirror_proc_offsets[p + 1] == mirror_proc_offsets[p]) {
+      continue;
+    }
+
+    sc_array_init_data (&pview, mirror_proc_mirrors + mirror_proc_offsets[p],
+                        sizeof (p4est_locidx_t),
+                        mirror_proc_offsets[p + 1] - mirror_proc_offsets[p]);
+
+    /* for every mirror */
+    P4EST_ASSERT (first_mirror >= 0 && end_mirror >= 0);
+    for (zm = (size_t) first_mirror; zm < (size_t) end_mirror; zm++) {
+      int                 f, c;
+#ifdef P4_TO_P8
+      int                 e;
+#endif
+      p4est_quadrant_t   *mq = p4est_quadrant_array_index (mirrors,
+                                                           (size_t) mpf[zm]);
+      p4est_locidx_t      t = mq->p.piggy3.which_tree;
+
+      if (lnodes) {
+        /* construct adjacency via lnodes */
+        int                 v, vnodes = lnodes->vnodes;
+        p4est_locidx_t      qid = mq->p.piggy3.local_num;
+
+        for (v = 0; v < vnodes; v++) {
+          p4est_locidx_t      nid = lnodes->element_nodes[qid * vnodes + v];
+          p4est_locidx_t      qstart, qend, il;
+          int                 owner = -1;
+
+          qstart = ntq_offset[nid];
+          qend = ntq_offset[nid + 1];
+          P4EST_ASSERT (qend > qstart);
+
+          for (il = qstart; il < qend; il++) {
+            p4est_locidx_t      nqid = node_to_quad[il];
+            p4est_topidx_t      nt = node_to_tree[il];
+            p4est_quadrant_t    qtemp;
+            p4est_quadrant_t   *q;
+            int                 already_in_mirrors = 0;
+
+            P4EST_ASSERT ((double) nqid == (double) nqid);
+            P4EST_ASSERT ((double) qid == (double) qid);
+            if (nqid == qid) {
+              continue;
+            }
+            if (nqid < 0) {
+              /* ghost */
+              size_t              idx = (size_t) - (nqid + 1);
+              q = p4est_quadrant_array_index (&ghost->ghosts, idx);
+              owner = p4est_comm_find_owner (p4est, nt, q, mpirank);
+            }
+            else {
+              /* local */
+              ssize_t             idx;
+              p4est_tree_t       *tree =
+                p4est_tree_array_index (p4est->trees, nt);
+              q =
+                p4est_quadrant_array_index (&tree->quadrants,
+                                            nqid - tree->quadrants_offset);
+
+              owner = mpirank;
+              qtemp = *q;
+
+              qtemp.p.piggy3.which_tree = nt;
+              qtemp.p.piggy3.local_num = qid;
+
+              idx =
+                sc_array_bsearch (mirrors, &qtemp,
+                                  p4est_quadrant_compare_piggy);
+              if (idx >= 0) {
+                p4est_locidx_t      key = (p4est_locidx_t) idx;
+
+                idx = sc_array_bsearch (&pview, &key, p4est_locidx_compare);
+
+                if (idx >= 0) {
+                  already_in_mirrors = 1;
+                }
+              }
+            }
+            if (!already_in_mirrors && owner != p) {
+              p4est_ghost_expand_insert (q, nt, nqid < 0 ? -(nqid + 1) : nqid,
+                                         send_bufs, p, owner,
+                                         nqid < 0 ? 1 : 0);
+            }
+          }
+        }
+      }
+      else {
+        /* for every face */
+        for (f = 0; f < P4EST_FACES; f++) {
+          p4est_quadrant_t    nq;
+          p4est_topidx_t      nt;
+          int                 nf;
+
+          nf = (f ^ 1);
+          nt = t;
+
+          /* get the neighbor */
+          nt = p4est_quadrant_face_neighbor_extra (mq, t, f, &nq, &nf, conn);
+          if (nt == -1) {
+            continue;
+          }
+
+          nf = nf % P4EST_FACES;
+
+          /* add any quadrant that overlaps this neighbor and touches the mirror
+           * to the buffer */
+          p4est_ghost_expand_int (t, mq, nt, &nq, &pview, P4EST_CONNECT_FACE,
+                                  nf, tempquads, temptrees, p, p4est, ghost,
+                                  send_bufs);
+        }
+        if (btype == P4EST_CONNECT_FACE) {
+          continue;
+        }
+
+#ifdef P4_TO_P8
+        /* for every edge */
+        for (e = 0; e < P8EST_EDGES; e++) {
+          p4est_quadrant_t   *nq;
+          p4est_topidx_t      nt;
+          int                 ne;
+
+          nt = t;
+
+          /* get the neighbors */
+          p8est_quadrant_edge_neighbor_extra (mq, t, e, tempquads2,
+                                              temptrees2, npoints, conn);
+
+          /* for every neighbor */
+          for (zz = 0; zz < tempquads2->elem_count; zz++) {
+            nq = p4est_quadrant_array_index (tempquads2, zz);
+            nt = *((p4est_locidx_t *) sc_array_index (temptrees2, zz));
+            ne = *((int *) sc_array_index (npoints, zz));
+            ne = ne % P8EST_EDGES;
+
+            /* add any quadrant that overlaps this neighbor and touches the mirror
+             * to the buffer */
+            p4est_ghost_expand_int (t, mq, nt, nq, &pview, P8EST_CONNECT_EDGE,
+                                    ne, tempquads, temptrees, p, p4est, ghost,
+                                    send_bufs);
+
+          }
+          sc_array_reset (tempquads2);
+          sc_array_reset (temptrees2);
+          sc_array_reset (npoints);
+        }
+        if (btype == P8EST_CONNECT_EDGE) {
+          continue;
+        }
+#endif
+        /* for every corner */
+        for (c = 0; c < P4EST_CHILDREN; c++) {
+          p4est_quadrant_t   *nq;
+          p4est_topidx_t      nt;
+          int                 nc;
+
+          nt = t;
+
+          /* get the neighbors */
+          p4est_quadrant_corner_neighbor_extra (mq, t, c, tempquads2,
+                                                temptrees2, npoints, conn);
+
+          /* for every neighbor */
+          for (zz = 0; zz < tempquads2->elem_count; zz++) {
+            nq = p4est_quadrant_array_index (tempquads2, zz);
+            nt = *((p4est_locidx_t *) sc_array_index (temptrees2, zz));
+            nc = *((int *) sc_array_index (npoints, zz));
+
+            /* add any quadrant that overlaps this neighbor and touches the mirror
+             * to the buffer */
+            p4est_ghost_expand_int (t, mq, nt, nq, &pview,
+                                    P4EST_CONNECT_CORNER, nc, tempquads,
+                                    temptrees, p, p4est, ghost, send_bufs);
+          }
+          sc_array_reset (tempquads2);
+          sc_array_reset (temptrees2);
+          sc_array_reset (npoints);
+        }
+      }
+    }
+
+    sc_array_reset (&pview);
+  }
+  if (lnodes) {
+    P4EST_FREE (ntq_offset);
+    P4EST_FREE (node_to_quad);
+    P4EST_FREE (node_to_tree);
+  }
+  sc_array_destroy (tempquads);
+  sc_array_destroy (temptrees);
+  sc_array_destroy (tempquads2);
+  sc_array_destroy (temptrees2);
+  sc_array_destroy (npoints);
+
+  /* Send the counts of ghosts that are going to be sent */
+  new_count = 0;
+  for (p = 0, peer = 0; p < mpisize; p++) {
+    buf = (sc_array_t *) sc_array_index_int (send_bufs, p);
+
+    if (mirror_proc_offsets[p + 1] == mirror_proc_offsets[p]) {
+      continue;
+    }
+
+    if (buf->elem_count) {
+      sc_array_sort (buf, p4est_quadrant_compare_piggy);
+      sc_array_uniq (buf, p4est_quadrant_compare_piggy_proc);
+    }
+    send_counts[peer] = (p4est_locidx_t) buf->elem_count;
+    new_count += send_counts[peer];
+    P4EST_ASSERT (p != mpirank);
+    P4EST_LDEBUGF ("ghost layer expand post count send to %d\n", p);
+    mpiret = MPI_Isend (send_counts + peer, 1, P4EST_MPI_LOCIDX, p,
+                        P4EST_COMM_GHOST_EXPAND_COUNT, comm, send_request +
+                        peer);
+    SC_CHECK_MPI (mpiret);
+    peer++;
+  }
+  P4EST_ASSERT (peer == num_peers);
+  P4EST_VERBOSEF ("Total new ghosts to send %lld\n", (long long) new_count);
+
+  /* Wait for the counts */
+  if (num_peers > 0) {
+    mpiret = MPI_Waitall (num_peers, recv_request, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+
+    mpiret = MPI_Waitall (num_peers, send_request, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (p = 0; p < num_peers; ++p) {
+    P4EST_ASSERT (recv_request[p] == MPI_REQUEST_NULL);
+  }
+  for (p = 0; p < num_peers; ++p) {
+    P4EST_ASSERT (send_request[p] == MPI_REQUEST_NULL);
+  }
+#endif
+
+  /* Count ghosts */
+  for (peer = 0, num_new_ghosts = 0; peer < num_peers; ++peer) {
+    num_new_ghosts += recv_counts[peer];        /* same type */
+  }
+  P4EST_VERBOSEF ("Total new ghosts to receive %lld\n",
+                  (long long) num_new_ghosts);
+
+  /* Allocate space for the ghosts */
+  old_num_ghosts = (p4est_locidx_t) ghost_layer->elem_count;
+  sc_array_resize (ghost_layer, (size_t) (old_num_ghosts + num_new_ghosts));
+
+  /* Post receives for the ghosts */
+  for (p = 0, peer = 0, ghost_offset = old_num_ghosts; p < mpisize; p++) {
+
+    if (mirror_proc_offsets[p + 1] == mirror_proc_offsets[p]) {
+      continue;
+    }
+
+    if (recv_counts[peer]) {
+      P4EST_LDEBUGF
+        ("ghost layer expand post ghost receive %lld quadrants from %d\n",
+         (long long) recv_counts[peer], p);
+      mpiret =
+        MPI_Irecv (ghost_layer->array +
+                   ghost_offset * sizeof (p4est_quadrant_t),
+                   (int) (recv_counts[peer] * sizeof (p4est_quadrant_t)),
+                   MPI_BYTE, p, P4EST_COMM_GHOST_EXPAND_LOAD, comm,
+                   recv_load_request + peer);
+
+      SC_CHECK_MPI (mpiret);
+      ghost_offset += recv_counts[peer];
+    }
+    else {
+      recv_load_request[peer] = MPI_REQUEST_NULL;
+    }
+    peer++;
+  }
+  P4EST_ASSERT (ghost_offset == old_num_ghosts + num_new_ghosts);
+
+  /* Send the ghosts */
+  for (p = 0, peer = 0; p < mpisize; p++) {
+    if (mirror_proc_offsets[p + 1] == mirror_proc_offsets[p]) {
+      continue;
+    }
+    buf = (sc_array_t *) sc_array_index (send_bufs, p);
+    if (buf->elem_count > 0) {
+      P4EST_ASSERT ((p4est_locidx_t) buf->elem_count == send_counts[peer]);
+      P4EST_LDEBUGF
+        ("ghost layer expand post ghost send %lld quadrants to %d\n",
+         (long long) send_counts[peer], p);
+      mpiret =
+        MPI_Isend (buf->array,
+                   (int) (send_counts[peer] * sizeof (p4est_quadrant_t)),
+                   MPI_BYTE, p, P4EST_COMM_GHOST_EXPAND_LOAD, comm,
+                   send_load_request + peer);
+      SC_CHECK_MPI (mpiret);
+    }
+    else {
+      send_load_request[peer] = MPI_REQUEST_NULL;
+    }
+    peer++;
+  }
+
+  /* Wait for everything */
+  if (num_peers > 0) {
+    mpiret = MPI_Waitall (num_peers, recv_load_request, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+
+    mpiret = MPI_Waitall (num_peers, send_load_request, MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (p = 0; p < num_peers; p++) {
+    P4EST_ASSERT (recv_load_request[p] == MPI_REQUEST_NULL);
+  }
+  for (p = 0; p < num_peers; p++) {
+    P4EST_ASSERT (send_load_request[p] == MPI_REQUEST_NULL);
+  }
+#endif
+
+  /* Clean up */
+  P4EST_FREE (recv_counts);
+  P4EST_FREE (recv_request);
+  P4EST_FREE (send_request);
+
+  /* sift bridges out of send buffers so that we can reuse buffers when
+   * updating mirrors*/
+  for (p = 0; p < mpisize; p++) {
+    size_t              count;
+    p4est_quadrant_t   *q1, *q2;
+
+    buf = (sc_array_t *) sc_array_index_int (send_bufs, p);
+    count = buf->elem_count;
+
+    if (!count) {
+      continue;
+    }
+
+    q1 = p4est_quadrant_array_index (buf, 0);
+    for (zz = 0; zz < buf->elem_count; zz++) {
+      q2 = p4est_quadrant_array_index (buf, zz);
+      P4EST_ASSERT (p4est_quadrant_is_valid (q2));
+      if (p4est_comm_is_owner (p4est, q2->p.which_tree, q2, mpirank)) {
+#ifdef P4EST_ENABLE_DEBUG
+        ssize_t             idx;
+
+        idx = sc_array_bsearch (mirrors, q2, p4est_quadrant_compare_piggy);
+
+        if (idx >= 0) {
+          ssize_t             idx2;
+          sc_array_t          pview;
+          p4est_locidx_t      locidx = (p4est_locidx_t) idx;
+          sc_array_init_data (&pview,
+                              mirror_proc_mirrors + mirror_proc_offsets[p],
+                              sizeof (p4est_locidx_t),
+                              mirror_proc_offsets[p + 1] -
+                              mirror_proc_offsets[p]);
+          idx2 = sc_array_bsearch (&pview, &locidx, p4est_locidx_compare);
+          P4EST_ASSERT (idx2 < 0);
+        }
+#endif
+        if (q1 != q2) {
+          *(q1++) = *q2;
+        }
+        else {
+          q1++;
+        }
+      }
+      else {
+        count--;
+      }
+    }
+    P4EST_LDEBUGF ("ghost layer expand sending %lld new non-bridges to %d\n",
+                   (long long) count, p);
+    sc_array_resize (buf, count);
+  }
+
+  if (num_new_ghosts) {
+    p4est_quadrant_t   *q1, *q2;
+
+    q1 = p4est_quadrant_array_index (ghost_layer, old_num_ghosts);
+
+    /* sift out the bridges */
+    for (zz = old_num_ghosts; zz < ghost_layer->elem_count; zz++) {
+      q2 = p4est_quadrant_array_index (ghost_layer, zz);
+
+      if (p4est_comm_is_owner (p4est, q2->p.which_tree, q2, mpirank)) {
+        /* this is a bridge: it is already a mirror for a proc other
+         * than p */
+        int                 target = q2->p.piggy1.owner_rank;
+        ssize_t             idx, idx2;
+        p4est_locidx_t      locidx;
+        sc_array_t          pview;
+
+        P4EST_ASSERT (0 <= target && target < mpisize);
+        P4EST_ASSERT (target != mpirank);
+
+        num_new_ghosts--;
+        idx = sc_array_bsearch (mirrors, q2, p4est_quadrant_compare_piggy);
+        P4EST_ASSERT (idx >= 0);
+        /* does the target already know about this ? */
+        locidx = (p4est_locidx_t) idx;
+        sc_array_init_data (&pview,
+                            mirror_proc_mirrors + mirror_proc_offsets[target],
+                            sizeof (p4est_locidx_t),
+                            mirror_proc_offsets[target + 1] -
+                            mirror_proc_offsets[target]);
+        idx2 = sc_array_bsearch (&pview, &locidx, p4est_locidx_compare);
+        sc_array_reset (&pview);
+
+        if (idx2 < 0) {
+          /* if the target doesn't already know about it, put it in send_bufs
+           * */
+          p4est_quadrant_t   *q3, *q4;
+
+          q3 = p4est_quadrant_array_index (mirrors, (size_t) idx);
+          P4EST_ASSERT (p4est_quadrant_is_equal_piggy (q2, q3));
+          buf = (sc_array_t *) sc_array_index_int (send_bufs, target);
+          q4 = (p4est_quadrant_t *) sc_array_push (buf);
+          *q4 = *q3;
+        }
+      }
+      else {
+        if (q1 != q2) {
+          *(q1++) = *q2;
+        }
+        else {
+          q1++;
+        }
+      }
+    }
+
+    P4EST_ASSERT (num_new_ghosts >= 0);
+
+    for (p = 0; p < mpisize; p++) {
+      buf = (sc_array_t *) sc_array_index_int (send_bufs, p);
+
+      sc_array_sort (buf, p4est_quadrant_compare_piggy);
+      sc_array_uniq (buf, p4est_quadrant_compare_piggy);
+    }
+
+    if (num_new_ghosts) {
+      /* update the ghost layer */
+      sc_array_resize (ghost_layer,
+                       (size_t) (old_num_ghosts + num_new_ghosts));
+      sc_array_sort (ghost_layer, p4est_quadrant_compare_piggy);
+      sc_array_uniq (ghost_layer, p4est_quadrant_compare_piggy);
+
+      num_new_ghosts = ghost_layer->elem_count - old_num_ghosts;
+
+      if (num_new_ghosts) {
+
+        P4EST_LDEBUGF ("ghost layer expand receiving %lld new non-bridges\n",
+                       (long long) num_new_ghosts);
+        /* calculate tree offsets */
+        sc_array_init (&split, sizeof (size_t));
+        sc_array_split (ghost_layer, &split,
+                        (size_t) conn->num_trees, ghost_tree_type, NULL);
+        P4EST_ASSERT (split.elem_count == (size_t) conn->num_trees + 1);
+        for (t = 0; t <= conn->num_trees; ++t) {
+          ppz = (size_t *) sc_array_index (&split, (size_t) t);
+          ghost->tree_offsets[t] = *ppz;
+        }
+        sc_array_reset (&split);
+
+        /* calculate proc offsets */
+        sc_array_init (&split, sizeof (size_t));
+        sc_array_split (ghost_layer, &split,
+                        (size_t) mpisize, ghost_proc_type, p4est);
+        P4EST_ASSERT (split.elem_count == (size_t) mpisize + 1);
+        for (p = 0; p <= mpisize; p++) {
+          ppz = (size_t *) sc_array_index (&split, (size_t) p);
+          proc_offsets[p] = (p4est_locidx_t) (*ppz);
+        }
+        sc_array_reset (&split);
+      }
+    }
+  }
+
+  /* we're going to build new mirrors structures, then destroy the old
+   * structures later */
+  new_mirrors = sc_array_new_size (mirrors->elem_size, mirrors->elem_count);
+  sc_array_copy (new_mirrors, mirrors);
+  old_num_mirrors = (p4est_locidx_t) mirrors->elem_count;
+  for (p = 0; p < mpisize; p++) {
+    /* add all of the potentially new mirrors */
+    buf = (sc_array_t *) sc_array_index_int (send_bufs, p);
+    size_t              oldsize;
+
+    if (!buf->elem_count) {
+      continue;
+    }
+    oldsize = new_mirrors->elem_count;
+    sc_array_resize (new_mirrors, oldsize + buf->elem_count);
+    memcpy (new_mirrors->array + oldsize * new_mirrors->elem_size,
+            buf->array, buf->elem_count * buf->elem_size);
+  }
+  sc_array_sort (new_mirrors, p4est_quadrant_compare_piggy);
+  sc_array_uniq (new_mirrors, p4est_quadrant_compare_piggy);
+  new_num_mirrors = (p4est_locidx_t) new_mirrors->elem_count;
+  P4EST_ASSERT (new_num_mirrors >= old_num_mirrors);
+
+  if (new_num_mirrors > old_num_mirrors) {
+    /* update mirror_tree_offsets */
+    sc_array_init (&split, sizeof (size_t));
+    sc_array_split (new_mirrors, &split,
+                    (size_t) conn->num_trees, ghost_tree_type, NULL);
+    P4EST_ASSERT (split.elem_count == (size_t) conn->num_trees + 1);
+    for (t = 0; t <= conn->num_trees; ++t) {
+      ppz = (size_t *) sc_array_index (&split, (size_t) t);
+      mirror_tree_offsets[t] = *ppz;
+    }
+    sc_array_reset (&split);
+  }
+
+  /* update mirror_proc_fronts */
+  nmpfa = sc_array_new (sizeof (p4est_locidx_t));
+  for (p = 0; p < mpisize; p++) {
+    size_t              offset = nmpfa->elem_count;
+    buf = (sc_array_t *) sc_array_index_int (send_bufs, p);
+
+    sc_array_resize (nmpfa, offset + buf->elem_count);
+    mpfo[p] = (p4est_locidx_t) offset;
+
+    for (zz = 0; zz < buf->elem_count; zz++) {
+      ssize_t             idx;
+      p4est_quadrant_t   *q1 = p4est_quadrant_array_index (buf, zz);
+      p4est_locidx_t     *lp =
+        (p4est_locidx_t *) sc_array_index (nmpfa, offset + zz);
+
+      idx = sc_array_bsearch (new_mirrors, q1, p4est_quadrant_compare_piggy);
+      P4EST_ASSERT (idx >= 0);
+      *lp = (p4est_locidx_t) idx;
+    }
+
+    sc_array_reset (buf);
+  }
+  mpfo[mpisize] = nmpfa->elem_count;
+  P4EST_FREE (mpf);
+  ghost->mirror_proc_fronts = mpf =
+    P4EST_ALLOC (p4est_locidx_t, nmpfa->elem_count);
+  memcpy (mpf, nmpfa->array, nmpfa->elem_size * nmpfa->elem_count);
+  sc_array_destroy (nmpfa);
+  sc_array_destroy (send_bufs);
+
+  /* update mirror_proc_mirrors */
+  nmpma = sc_array_new (sizeof (p4est_locidx_t));
+  for (p = 0; p < mpisize; p++) {
+    size_t              frontsize = mpfo[p + 1] - mpfo[p];
+    size_t              offset = nmpma->elem_count;
+    p4est_locidx_t      old_offset = mirror_proc_offsets[p];
+    p4est_locidx_t      old_count = mirror_proc_offsets[p + 1] - old_offset;
+
+    P4EST_ASSERT (old_count >= 0);
+    mirror_proc_offsets[p] = offset;
+
+    P4EST_LDEBUGF
+      ("ghost layer expanded with proc %d: send %lld receive %lld\n",
+       p, (long long) old_count + frontsize,
+       (long long) proc_offsets[p + 1] - proc_offsets[p]);
+    sc_array_resize (nmpma, offset + old_count + frontsize);
+    memcpy (nmpma->array + nmpma->elem_size * offset,
+            mpf + mpfo[p], sizeof (p4est_locidx_t) * frontsize);
+
+    if (old_count) {
+      sc_array_t          pview;
+      for (zz = 0; zz < (size_t) old_count; zz++) {
+        ssize_t             idx;
+        p4est_quadrant_t   *q1 = p4est_quadrant_array_index (mirrors,
+                                                             mirror_proc_mirrors
+                                                             [zz +
+                                                              old_offset]);
+        p4est_locidx_t     *lp =
+          (p4est_locidx_t *) sc_array_index (nmpma, offset + frontsize + zz);
+
+        idx =
+          sc_array_bsearch (new_mirrors, q1, p4est_quadrant_compare_piggy);
+        P4EST_ASSERT (idx >= 0);
+        *lp = (p4est_locidx_t) idx;
+      }
+
+      sc_array_init_view (&pview, nmpma, offset, old_count + frontsize);
+      sc_array_sort (&pview, p4est_locidx_compare);
+      sc_array_reset (&pview);
+    }
+  }
+  mirror_proc_offsets[mpisize] = nmpma->elem_count;
+  P4EST_FREE (mirror_proc_mirrors);
+  ghost->mirror_proc_mirrors = mirror_proc_mirrors = P4EST_ALLOC
+    (p4est_locidx_t, nmpma->elem_count);
+  memcpy (mirror_proc_mirrors, nmpma->array,
+          nmpma->elem_size * nmpma->elem_count);
+  sc_array_destroy (nmpma);
+
+  sc_array_resize (mirrors, new_mirrors->elem_count);
+  sc_array_copy (mirrors, new_mirrors);
+  sc_array_destroy (new_mirrors);
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (p = 0; p < mpisize; p++) {
+    int                 ghost_count =
+      ghost->proc_offsets[p + 1] - ghost->proc_offsets[p];
+    int                 mirror_count =
+      ghost->mirror_proc_offsets[p + 1] - ghost->mirror_proc_offsets[p];
+
+    P4EST_ASSERT ((mirror_count && ghost_count)
+                  || (!mirror_count && !ghost_count));
+
+  }
+#endif
+  P4EST_ASSERT (p4est_ghost_is_valid (p4est, ghost));
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_ghost_expand\n");
+#endif
+}
+
+void
+p4est_ghost_expand (p4est_t * p4est, p4est_ghost_t * ghost)
+{
+  p4est_ghost_expand_internal (p4est, NULL, ghost);
+}
+
+void
+p4est_ghost_expand_by_lnodes (p4est_t * p4est, p4est_lnodes_t * lnodes,
+                              p4est_ghost_t * ghost)
+{
+  p4est_ghost_expand_internal (p4est, lnodes, ghost);
+}
+
+int
+p4est_ghost_is_valid (p4est_t * p4est, p4est_ghost_t * ghost)
+{
+  const p4est_topidx_t num_trees = ghost->num_trees;
+  const int           mpisize = ghost->mpisize;
+  int                 i, mpiret, retval;
+  size_t              view_length, proc_length;
+  p4est_locidx_t      proc_offset;
+  sc_array_t          array, *workspace, *requests;
+  uint64_t           *checksums_recv, *checksums_send;
+
+  /* check if the last entries of the offset arrays are the element count
+   * of ghosts/mirrors array. */
+  if ((size_t) ghost->tree_offsets[num_trees] != ghost->ghosts.elem_count
+      || (size_t) ghost->proc_offsets[mpisize] != ghost->ghosts.elem_count
+      || (size_t) ghost->mirror_tree_offsets[num_trees] !=
+      ghost->mirrors.elem_count) {
+    return 0;
+  }
+
+  /* check if quadrants in ghost and mirror layer are
+   * in p4est_quadrant_compare_piggy order.
+   * Also check if tree_offsets, proc_offsets, mirror_tree_offsets
+   * and mirror_proc_offsets are sorted.
+   */
+  if (!sc_array_is_sorted (&ghost->ghosts, p4est_quadrant_compare_piggy) ||
+      !sc_array_is_sorted (&ghost->mirrors, p4est_quadrant_compare_piggy)
+      || !sc_array_is_sorted (&ghost->mirrors,
+                              p4est_quadrant_compare_local_num)) {
+    return 0;
+  }
+  sc_array_init_data (&array, ghost->tree_offsets, sizeof (p4est_locidx_t),
+                      num_trees + 1);
+  if (!sc_array_is_sorted (&array, p4est_locidx_compare))
+    return 0;
+  sc_array_init_data (&array, ghost->proc_offsets, sizeof (p4est_locidx_t),
+                      mpisize + 1);
+  if (!sc_array_is_sorted (&array, p4est_locidx_compare))
+    return 0;
+  sc_array_init_data (&array, ghost->mirror_tree_offsets,
+                      sizeof (p4est_locidx_t), num_trees + 1);
+  if (!sc_array_is_sorted (&array, p4est_locidx_compare))
+    return 0;
+  sc_array_init_data (&array, ghost->mirror_proc_offsets,
+                      sizeof (p4est_locidx_t), mpisize + 1);
+  if (!sc_array_is_sorted (&array, p4est_locidx_compare))
+    return 0;
+
+  /* check if local number in piggy3 data member of the quadrants in ghost is
+   * ascending within each rank.
+   */
+  for (i = 0; i < mpisize; i++) {
+    proc_offset = ghost->proc_offsets[i];
+    view_length = (size_t) (ghost->proc_offsets[i + 1] - proc_offset);
+    sc_array_init_view (&array, &ghost->ghosts, (size_t) proc_offset,
+                        view_length);
+    if (!sc_array_is_sorted (&array, p4est_quadrant_compare_local_num)) {
+      return 0;
+    }
+  }
+
+  /* check if mirror_proc_offsets is ascending within each rank
+   */
+  for (i = 0; i < mpisize; i++) {
+    proc_offset = ghost->mirror_proc_offsets[i];
+    proc_length = (size_t) (ghost->mirror_proc_offsets[i + 1] - proc_offset);
+    sc_array_init_data (&array, ghost->mirror_proc_mirrors + proc_offset,
+                        sizeof (p4est_locidx_t), proc_length);
+    if (!sc_array_is_sorted (&array, p4est_locidx_compare)) {
+      return 0;
+    }
+  }
+
+  /* compare checksums of ghosts with checksums of mirrors */
+  checksums_recv = P4EST_ALLOC (uint64_t, mpisize);
+  checksums_send = P4EST_ALLOC (uint64_t, mpisize);
+  requests = sc_array_new (sizeof (sc_MPI_Request));
+  workspace = sc_array_new (sizeof (p4est_quadrant_t));
+  for (i = 0; i < mpisize; i++) {
+    p4est_locidx_t      count;
+    sc_MPI_Request     *req;
+
+    proc_offset = ghost->proc_offsets[i];
+    count = ghost->proc_offsets[i + 1] - proc_offset;
+
+    if (count) {
+      req = (sc_MPI_Request *) sc_array_push (requests);
+      mpiret = sc_MPI_Irecv (&checksums_recv[i], 1, sc_MPI_LONG_LONG_INT, i,
+                             P4EST_COMM_GHOST_CHECKSUM, p4est->mpicomm, req);
+      SC_CHECK_MPI (mpiret);
+    }
+
+    proc_offset = ghost->mirror_proc_offsets[i];
+    count = ghost->mirror_proc_offsets[i + 1] - proc_offset;
+
+    if (count) {
+      p4est_locidx_t      jl;
+
+      sc_array_truncate (workspace);
+
+      for (jl = proc_offset; jl < proc_offset + count; jl++) {
+        p4est_locidx_t      idx;
+        p4est_quadrant_t   *q1, *q2;
+
+        idx = ghost->mirror_proc_mirrors[jl];
+
+        q1 = p4est_quadrant_array_index (&ghost->mirrors, (size_t) idx);
+        q2 = p4est_quadrant_array_push (workspace);
+        *q2 = *q1;
+      }
+
+      checksums_send[i] =
+        (uint64_t) p4est_quadrant_checksum (workspace, NULL, 0);
+
+      req = (sc_MPI_Request *) sc_array_push (requests);
+      mpiret = sc_MPI_Isend (&checksums_send[i], 1, sc_MPI_LONG_LONG_INT, i,
+                             P4EST_COMM_GHOST_CHECKSUM, p4est->mpicomm, req);
+      SC_CHECK_MPI (mpiret);
+    }
+  }
+
+  mpiret = sc_MPI_Waitall (requests->elem_count, (sc_MPI_Request *)
+                           requests->array, sc_MPI_STATUSES_IGNORE);
+  SC_CHECK_MPI (mpiret);
+  sc_array_destroy (workspace);
+  sc_array_destroy (requests);
+  P4EST_FREE (checksums_send);
+
+  retval = 1;
+  for (i = 0; i < mpisize; i++) {
+    p4est_locidx_t      count;
+
+    proc_offset = ghost->proc_offsets[i];
+    count = ghost->proc_offsets[i + 1] - proc_offset;
+
+    if (count) {
+      sc_array_t          view;
+      uint64_t            thiscrc;
+
+      sc_array_init_view (&view, &ghost->ghosts, (size_t) proc_offset,
+                          (size_t) count);
+
+      thiscrc = (uint64_t) p4est_quadrant_checksum (&view, NULL, 0);
+      if (thiscrc != checksums_recv[i]) {
+        P4EST_LERRORF ("Ghost layer checksum mismatch: "
+                       "proc %d, my checksum %llu, their checksum %llu\n",
+                       i, (long long unsigned) thiscrc,
+                       (long long unsigned) checksums_recv[i]);
+        retval = 0;
+      }
+    }
+  }
+  P4EST_FREE (checksums_recv);
+  return retval;
+}
diff --git a/src/p4est_ghost.h b/src/p4est_ghost.h
new file mode 100644
index 0000000..23abb4d
--- /dev/null
+++ b/src/p4est_ghost.h
@@ -0,0 +1,305 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_ghost.h
+ *
+ * passing quadrants and data to neighboring processes
+ *
+ * \ingroup p4est
+ */
+
+#ifndef P4EST_GHOST_H
+#define P4EST_GHOST_H
+
+#include <p4est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** quadrants that neighbor the local domain */
+typedef struct
+{
+  int                 mpisize;
+  p4est_topidx_t      num_trees;
+  p4est_connect_type_t btype; /**< which neighbors are in the ghost layer */
+
+  /** An array of quadrants which make up the ghost layer around \a
+   * p4est.  Their piggy3 data member is filled with their owner's tree
+   * and local number (cumulative over trees).  Quadrants are ordered in \c
+   * p4est_quadrant_compare_piggy order.  These are quadrants inside the
+   * neighboring tree, i.e., \c p4est_quadrant_is_inside() is true for the
+   * quadrant and the neighboring tree.
+   */
+  sc_array_t          ghosts; /**< array of p4est_quadrant_t type */
+  p4est_locidx_t     *tree_offsets;     /**< num_trees + 1 ghost indices */
+  p4est_locidx_t     *proc_offsets;     /**< mpisize + 1 ghost indices */
+
+  /** An array of local quadrants that touch the parallel boundary from the
+   * inside, i.e., that are ghosts in the perspective of at least one other
+   * processor.  The storage convention is the same as for \c ghosts above.
+   */
+  sc_array_t          mirrors; /**< array of p4est_quadrant_t type */
+  p4est_locidx_t     *mirror_tree_offsets;      /**< num_trees + 1 mirror indices */
+  p4est_locidx_t     *mirror_proc_mirrors;      /**< indices into mirrors grouped by
+                                                   outside processor rank and
+                                                   ascending within each rank */
+  p4est_locidx_t     *mirror_proc_offsets;      /**< mpisize + 1 indices into 
+                                                   mirror_proc_mirrors */
+
+  p4est_locidx_t     *mirror_proc_fronts;       /**< like mirror_proc_mirrors,
+                                                   but limited to the
+                                                   outermost octants.  This is
+                                                   NULL until
+                                                   p4est_ghost_expand is
+                                                   called */
+  p4est_locidx_t     *mirror_proc_front_offsets;        /**< NULL until
+                                                           p4est_ghost_expand is
+                                                           called */
+}
+p4est_ghost_t;
+
+/** Examine if a ghost structure is valid.
+ * Test if within a ghost-structure the array ghosts is in
+ * p4est_quadrant_compare_piggy order.
+ * Test if local_num in piggy3 data member of the quadrants in ghosts and
+ * mirrors are in ascending order (ascending within each rank for ghost).
+ *
+ * Test if the p4est_locidx_t arrays are in ascending order
+ * (for mirror_proc_mirrors ascending within each rank)
+ * \param [in] p4est    the forest.
+ * \param [in] ghost    Ghost layer structure.
+ * \return true if \a ghost is valid
+ */
+int                 p4est_ghost_is_valid (p4est_t *p4est, p4est_ghost_t * ghost);
+
+/** Calculate the memory usage of the ghost layer.
+ * \param [in] ghost    Ghost layer structure.
+ * \return              Memory used in bytes.
+ */
+size_t              p4est_ghost_memory_used (p4est_ghost_t * ghost);
+
+/** Gets the processor id of a quadrant's owner.
+ * The quadrant can lie outside of a tree across faces (and only faces).
+ *
+ * \param [in] p4est  The forest in which to search for a quadrant.
+ * \param [in] treeid The tree to which the quadrant belongs.
+ * \param [in] face   Supply a face direction if known, or -1 otherwise.
+ * \param [in] q      The quadrant that is being searched for.
+ *
+ * \return Processor id of the owner
+ *                or -1 if the quadrant lies outside of the mesh.
+ *
+ * \warning Does not work for tree edge or corner neighbors.
+ */
+int                 p4est_quadrant_find_owner (p4est_t * p4est,
+                                               p4est_topidx_t treeid,
+                                               int face,
+                                               const p4est_quadrant_t * q);
+
+/** Builds the ghost layer.
+ *
+ * This will gather the quadrants from each neighboring proc to build
+ * one layer of face and corner based ghost elements around the ones they own.
+ *
+ * \param [in] p4est            The forest for which the ghost layer will be
+ *                              generated.
+ * \param [in] btype            Which ghosts to include (across face, corner
+ *                              or full).
+ * \return                      A fully initialized ghost layer.
+ */
+p4est_ghost_t      *p4est_ghost_new (p4est_t * p4est,
+                                     p4est_connect_type_t btype);
+
+/** Frees all memory used for the ghost layer. */
+void                p4est_ghost_destroy (p4est_ghost_t * ghost);
+
+/** Conduct binary search for exact match on a range of the ghost layer.
+ * \param [in] ghost            The ghost layer.
+ * \param [in] which_proc       The owner of the searched quadrant.  Can be -1.
+ * \param [in] which_tree       The tree of the searched quadrant.  Can be -1.
+ * \param [in] q                Valid quadrant is searched in the ghost layer.
+ * \return                      Offset in the ghost layer, or -1 if not found.
+ */
+ssize_t             p4est_ghost_bsearch (p4est_ghost_t * ghost,
+                                         int which_proc,
+                                         p4est_topidx_t which_tree,
+                                         const p4est_quadrant_t * q);
+
+/** Conduct binary search for ancestor on range of the ghost layer.
+ * \param [in] ghost            The ghost layer.
+ * \param [in] which_proc       The owner of the searched quadrant.  Can be -1.
+ * \param [in] which_tree       The tree of the searched quadrant.  Can be -1.
+ * \param [in] q                Valid quadrant's ancestor is searched.
+ * \return                      Offset in the ghost layer, or -1 if not found.
+ */
+ssize_t             p4est_ghost_contains (p4est_ghost_t * ghost,
+                                          int which_proc,
+                                          p4est_topidx_t which_tree,
+                                          const p4est_quadrant_t * q);
+
+/** Checks if quadrant exists in the local forest or the ghost layer.
+ *
+ * For quadrants across tree boundaries it checks if the quadrant exists
+ * across any face, but not across corners.
+ *
+ * \param [in]  p4est        The forest in which to search for \a q.
+ * \param [in]  ghost        The ghost layer in which to search for \a q.
+ * \param [in]  treeid       The tree to which \a q belongs.
+ * \param [in]  q            The quadrant that is being searched for.
+ * \param [in,out] face      On input, face id across which \a q was created.
+ *                           On output, the neighbor's face number augmented
+ *                           by orientation, so face is in 0..7.
+ * \param [in,out] hang      If not NULL, signals that q is bigger than
+ *                           the quadrant it came from.  The child id
+ *                           of that originating quadrant is passed into hang.
+ *                           On output, hang holds the hanging face number
+ *                           of \a q that is in contact with its originator.
+ * \param [out] owner_rank   Filled with the rank of the owner if it is found
+ *                           and undefined otherwise.
+ *
+ * \return      Returns the local number of \a q if the quadrant exists
+ *              in the local forest or in the ghost_layer.  Otherwise,
+ *              returns -2 for a domain boundary and -1 if not found.
+ */
+p4est_locidx_t      p4est_face_quadrant_exists (p4est_t * p4est,
+                                                p4est_ghost_t * ghost,
+                                                p4est_topidx_t treeid,
+                                                const p4est_quadrant_t * q,
+                                                int *face, int *hang,
+                                                int *owner_rank);
+
+/** Checks if quadrant exists in the local forest or the ghost layer.
+ *
+ * For quadrants across tree corners it checks if the quadrant exists
+ * in any of the corner neighbors, thus it can execute multiple queries.
+ *
+ * \param [in]  p4est        The forest in which to search for \a q
+ * \param [in]  ghost        The ghost layer in which to search for \a q
+ * \param [in]  treeid       The tree to which \a q belongs (can be extended).
+ * \param [in]  q            The quadrant that is being searched for.
+ * \param [in,out] exists_arr Must exist and be of of elem_size = sizeof (int)
+ *                           for inter-tree corner cases.  Is resized by this
+ *                           function to one entry for each corner search
+ *                           and set to true/false depending on its existence
+ *                           in the local forest or ghost_layer.
+ * \param [in,out] rproc_arr If not NULL is filled with one rank per query.
+ * \param [in,out] rquad_arr If not NULL is filled with one quadrant per query.
+ *                           Its piggy3 member is defined as well.
+ *
+ * \return true if the quadrant exists in the local forest or in the
+ *                  ghost_layer, and false if doesn't exist in either.
+ */
+int                 p4est_quadrant_exists (p4est_t * p4est,
+                                           p4est_ghost_t * ghost,
+                                           p4est_topidx_t treeid,
+                                           const p4est_quadrant_t * q,
+                                           sc_array_t * exists_arr,
+                                           sc_array_t * rproc_arr,
+                                           sc_array_t * rquad_arr);
+
+/** Check a forest to see if it is balanced.
+ *
+ * This function builds the ghost layer and discards it when done.
+ *
+ * \param [in] p4est    The p4est to be tested.
+ * \param [in] btype    Balance type (face, corner or default, full).
+ * \return Returns true if balanced, false otherwise.
+ */
+int                 p4est_is_balanced (p4est_t * p4est,
+                                       p4est_connect_type_t btype);
+
+/** Compute the parallel checksum of a ghost layer.
+ * \param [in] p4est   The MPI information of this p4est will be used.
+ * \param [in] ghost   A ghost layer obtained from the p4est.
+ * \return             Parallel checksum on rank 0, 0 otherwise.
+ */
+unsigned            p4est_ghost_checksum (p4est_t * p4est,
+                                          p4est_ghost_t * ghost);
+
+/** Transfer data for local quadrants that are ghosts to other processors.
+ * Send the data stored in the quadrant's user_data.  This is either the
+ * pointer variable itself if \c p4est->data_size is 0, or the content of
+ * the referenced memory field if p4est->data_size is positive.
+ * \param [in] p4est            The forest used for reference.
+ * \param [in] ghost            The ghost layer used for reference.
+ * \param [in,out] ghost_data   Pre-allocated contiguous data for all ghost
+ *                              quadrants in sequence.  If p4est->data_size is
+ *                              0, must at least hold sizeof (void *) bytes for
+ *                              each, otherwise p4est->data_size each.
+ */
+void                p4est_ghost_exchange_data (p4est_t * p4est,
+                                               p4est_ghost_t * ghost,
+                                               void *ghost_data);
+
+/** Transfer data for local quadrants that are ghosts to other processors.
+ * The data size is the same for all quadrants and can be chosen arbitrarily.
+ * \param [in] p4est            The forest used for reference.
+ * \param [in] ghost            The ghost layer used for reference.
+ * \param [in] data_size        The data size to transfer per quadrant.
+ * \param [in] mirror_data      One data pointer per mirror quadrant as input. 
+ * \param [in,out] ghost_data   Pre-allocated contiguous data for all ghosts
+ *                              in sequence, which must hold at least \c
+ *                              data_size for each ghost.
+ */
+void                p4est_ghost_exchange_custom (p4est_t * p4est,
+                                                 p4est_ghost_t * ghost,
+                                                 size_t data_size,
+                                                 void **mirror_data,
+                                                 void *ghost_data);
+
+/** Transfer data for local quadrants that are ghosts to other processors.
+ * The data size is the same for all quadrants and can be chosen arbitrarily.
+ * This function restricts the transfer to a range of refinement levels.
+ * The memory for quadrants outside the level range is not dereferenced.
+ * \param [in] p4est            The forest used for reference.
+ * \param [in] ghost            The ghost layer used for reference.
+ * \param [in] minlevel         Level of the largest quads to be exchanged.
+ *                              Use <= 0 for no restriction.
+ * \param [in] maxlevel         Level of the smallest quads to be exchanged.
+ *                              Use >= P4EST_QMAXLEVEL for no restriction.
+ * \param [in] data_size        The data size to transfer per quadrant.
+ * \param [in] mirror_data      One data pointer per mirror quadrant as input. 
+ * \param [in,out] ghost_data   Pre-allocated contiguous data for all ghosts
+ *                              in sequence, which must hold at least \c
+ *                              data_size for each ghost.
+ */
+void                p4est_ghost_exchange_custom_levels (p4est_t * p4est,
+                                                        p4est_ghost_t * ghost,
+                                                        int minlevel,
+                                                        int maxlevel,
+                                                        size_t data_size,
+                                                        void **mirror_data,
+                                                        void *ghost_data);
+
+/** Expand the size of the ghost layer and mirrors by one additional layer of
+ * adjacency.
+ * \param [in] p4est            The forest from which the ghost layer was
+ *                              generated.
+ * \param [in,out] ghost        The ghost layer to be expanded.
+ */
+void                p4est_ghost_expand (p4est_t * p4est,
+                                        p4est_ghost_t * ghost);
+
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_GHOST_H */
diff --git a/src/p4est_io.c b/src/p4est_io.c
new file mode 100644
index 0000000..e170830
--- /dev/null
+++ b/src/p4est_io.c
@@ -0,0 +1,269 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Copyright (C) 2012 Carsten Burstedde
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_communication.h>
+#include <p4est_io.h>
+#else
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_communication.h>
+#include <p8est_io.h>
+#endif
+#include <sc_search.h>
+
+sc_array_t         *
+p4est_deflate_quadrants (p4est_t * p4est, sc_array_t ** data)
+{
+  const size_t        qsize = sizeof (p4est_qcoord_t);
+  const size_t        dsize = p4est->data_size;
+  size_t              qtreez, qz;
+  sc_array_t         *qarr, *darr;
+  p4est_topidx_t      tt;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *q;
+  p4est_qcoord_t     *qap;
+  char               *dap;
+
+  qarr = sc_array_new_size (qsize,
+                            (P4EST_DIM + 1) * p4est->local_num_quadrants);
+  qap = (p4est_qcoord_t *) qarr->array;
+  darr = NULL;
+  dap = NULL;
+  if (data != NULL) {
+    P4EST_ASSERT (dsize > 0);
+    darr = sc_array_new_size (dsize, p4est->local_num_quadrants);
+    dap = darr->array;
+  }
+  for (tt = p4est->first_local_tree; tt <= p4est->last_local_tree; ++tt) {
+    tree = p4est_tree_array_index (p4est->trees, tt);
+    qtreez = tree->quadrants.elem_count;
+    for (qz = 0; qz < qtreez; ++qz) {
+      q = p4est_quadrant_array_index (&tree->quadrants, qz);
+      *qap++ = q->x;
+      *qap++ = q->y;
+#ifdef P4_TO_P8
+      *qap++ = q->z;
+#endif
+      *qap++ = (p4est_qcoord_t) q->level;
+      if (data != NULL) {
+        memcpy (dap, q->p.user_data, dsize);
+        dap += dsize;
+      }
+    }
+  }
+  P4EST_ASSERT ((void *) qap ==
+                qarr->array + qarr->elem_size * qarr->elem_count);
+  if (data != NULL) {
+    P4EST_ASSERT (dap == darr->array + darr->elem_size * darr->elem_count);
+    *data = darr;
+  }
+  return qarr;
+}
+
+p4est_t            *
+p4est_inflate (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity,
+               const p4est_gloidx_t * global_first_quadrant,
+               const p4est_gloidx_t * pertree,
+               sc_array_t * quadrants, sc_array_t * data, void *user_pointer)
+{
+  const p4est_gloidx_t *gfq;
+  int                 i;
+  int                 mpiret;
+  int                 num_procs, rank;
+  p4est_topidx_t      num_trees, jt;
+  p4est_gloidx_t      gkey, gtreeskip, gtreeremain, gquadremain;
+  p4est_t            *p4est;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *q;
+#ifdef P4EST_ENABLE_DEBUG
+  int                 p;
+#endif
+  int8_t              ql, tml;
+  size_t              dsize;
+  size_t              gk1, gk2;
+  size_t              qz, zqoffset, zqthistree;
+  p4est_qcoord_t     *qap;
+  char               *dap;
+
+  P4EST_GLOBAL_PRODUCTION ("Into " P4EST_STRING "_inflate\n");
+  p4est_log_indent_push ();
+
+  P4EST_ASSERT (p4est_connectivity_is_valid (connectivity));
+  P4EST_ASSERT (global_first_quadrant != NULL);
+  P4EST_ASSERT (pertree != NULL);
+  P4EST_ASSERT (quadrants != NULL);
+  P4EST_ASSERT (quadrants->elem_size == sizeof (p4est_qcoord_t));
+  /* data may be NULL, in this case p4est->data_size will be 0 */
+  /* user_pointer may be anything, we don't look at it */
+
+  /* retrieve MPI information */
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  /* assign some data members */
+  p4est = P4EST_ALLOC_ZERO (p4est_t, 1);
+  p4est->mpicomm = mpicomm;
+  p4est->mpisize = num_procs;
+  p4est->mpirank = rank;
+  dsize = p4est->data_size = data == NULL ? 0 : data->elem_size;
+  dap = (char *) (data == NULL ? NULL : data->array);
+  qap = (p4est_locidx_t *) quadrants->array;
+  p4est->user_pointer = user_pointer;
+  p4est->connectivity = connectivity;
+  num_trees = connectivity->num_trees;
+
+  /* create global first quadrant offsets */
+  gfq = p4est->global_first_quadrant =
+    P4EST_ALLOC (p4est_gloidx_t, num_procs + 1);
+  memcpy (p4est->global_first_quadrant, global_first_quadrant,
+          (num_procs + 1) * sizeof (p4est_gloidx_t));
+#ifdef P4EST_ENABLE_DEBUG
+  P4EST_ASSERT (gfq[0] == 0);
+  for (p = 0; p < num_procs; ++p) {
+    P4EST_ASSERT (gfq[p] <= gfq[p + 1]);
+  }
+  P4EST_ASSERT (pertree[0] == 0);
+  for (jt = 0; jt < num_trees; ++jt) {
+    P4EST_ASSERT (pertree[jt] <= pertree[jt + 1]);
+  }
+  P4EST_ASSERT (gfq[num_procs] == pertree[num_trees]);
+#endif
+  gquadremain = gfq[rank + 1] - gfq[rank];
+  p4est->local_num_quadrants = (p4est_locidx_t) gquadremain;
+  p4est->global_num_quadrants = gfq[num_procs];
+  P4EST_ASSERT (quadrants->elem_count ==
+                (P4EST_DIM + 1) * (size_t) p4est->local_num_quadrants);
+  P4EST_ASSERT (data == NULL || data->elem_count ==
+                (size_t) p4est->local_num_quadrants);
+
+  /* allocate memory pools */
+  if (dsize > 0) {
+    p4est->user_data_pool = sc_mempool_new (dsize);
+  }
+  else {
+    p4est->user_data_pool = NULL;
+  }
+  p4est->quadrant_pool = sc_mempool_new (sizeof (p4est_quadrant_t));
+
+  /* find the first and last tree on this processor */
+  if (p4est->local_num_quadrants > 0) {
+    gkey = gfq[rank];
+    gk1 = sc_bsearch_range (&gkey, pertree, num_trees,
+                            sizeof (p4est_gloidx_t), p4est_gloidx_compare);
+    P4EST_ASSERT (gk1 < (size_t) num_trees);
+    gtreeskip = gkey - pertree[gk1];
+    gkey = gfq[rank + 1] - 1;
+    gk2 = sc_bsearch_range (&gkey, pertree, num_trees,
+                            sizeof (p4est_gloidx_t), p4est_gloidx_compare);
+    P4EST_ASSERT (gk1 <= gk2 && gk2 < (size_t) num_trees);
+    p4est->first_local_tree = (p4est_topidx_t) gk1;
+    p4est->last_local_tree = (p4est_topidx_t) gk2;
+  }
+  else {
+    gtreeskip = 0;
+    p4est->first_local_tree = -1;
+    p4est->last_local_tree = -2;
+  }
+
+  /* populate trees */
+  zqoffset = 0;
+  gquadremain = p4est->local_num_quadrants;
+  p4est->trees = sc_array_new_size (sizeof (p4est_tree_t), num_trees);
+  for (jt = 0; jt < num_trees; ++jt) {
+    /* all trees need at least some basic setup */
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    sc_array_init (&tree->quadrants, sizeof (p4est_quadrant_t));
+    P4EST_QUADRANT_INIT (&tree->first_desc);
+    P4EST_QUADRANT_INIT (&tree->last_desc);
+    tree->quadrants_offset = (p4est_locidx_t) zqoffset;
+    for (i = 0; i <= P4EST_QMAXLEVEL; ++i) {
+      tree->quadrants_per_level[i] = 0;
+    }
+    for (; i <= P4EST_MAXLEVEL; ++i) {
+      tree->quadrants_per_level[i] = -1;
+    }
+    q = NULL;
+    tree->maxlevel = 0;
+    if (jt >= p4est->first_local_tree && jt <= p4est->last_local_tree) {
+      /* this tree has local quadrants */
+      gtreeremain = pertree[jt + 1] - pertree[jt] - gtreeskip;
+      P4EST_ASSERT (gtreeremain > 0 && gquadremain > 0);
+      zqthistree = (size_t) SC_MIN (gtreeremain, gquadremain);
+      P4EST_ASSERT (zqthistree > 0);
+      sc_array_resize (&tree->quadrants, zqthistree);
+      tml = 0;
+      for (qz = 0; qz < zqthistree; ++qz) {
+        q = p4est_quadrant_array_index (&tree->quadrants, qz);
+        P4EST_QUADRANT_INIT (q);
+        q->x = *qap++;
+        q->y = *qap++;
+#ifdef P4_TO_P8
+        q->z = *qap++;
+#endif
+/* *INDENT-OFF* HORRIBLE indent bug */
+        q->level = ql = (int8_t) *qap++;
+/* *INDENT-ON* */
+        P4EST_ASSERT (ql >= 0 && ql <= P4EST_QMAXLEVEL);
+        ++tree->quadrants_per_level[ql];
+        tml = SC_MAX (tml, ql);
+        p4est_quadrant_init_data (p4est, jt, q, NULL);
+        if (data != NULL) {
+          memcpy (q->p.user_data, dap, dsize);
+          dap += dsize;
+        }
+        if (qz == 0) {
+          p4est_quadrant_first_descendant (q, &tree->first_desc,
+                                           P4EST_QMAXLEVEL);
+        }
+      }
+      p4est_quadrant_last_descendant (q, &tree->last_desc, P4EST_QMAXLEVEL);
+      tree->maxlevel = tml;
+      zqoffset += zqthistree;
+      gquadremain -= (p4est_gloidx_t) zqthistree;
+      gtreeskip = 0;
+    }
+  }
+  P4EST_ASSERT (zqoffset == (size_t) p4est->local_num_quadrants);
+  P4EST_ASSERT (gquadremain == 0);
+
+  /* communicate partition information */
+  p4est->global_first_position =
+    P4EST_ALLOC (p4est_quadrant_t, num_procs + 1);
+  p4est_comm_global_partition (p4est, NULL);
+
+  /* print more statistics */
+  P4EST_VERBOSEF ("total local quadrants %lld\n",
+                  (long long) p4est->local_num_quadrants);
+
+  P4EST_ASSERT (p4est_is_valid (p4est));
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_inflate\n");
+
+  return p4est;
+}
diff --git a/src/p4est_io.h b/src/p4est_io.h
new file mode 100644
index 0000000..dfc360d
--- /dev/null
+++ b/src/p4est_io.h
@@ -0,0 +1,68 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Copyright (C) 2012 Carsten Burstedde
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_IO_H
+#define P4EST_IO_H
+
+#include <p4est.h>
+
+/** Extract processor local quadrants' x y level data.
+ * Optionally extracts the quadrant data as well into a separate array.
+ * \param [in] p4est    The forest is not modified.
+ * \param [in,out] data If not NULL, pointer to a pointer that will be set
+ *                      to a newly allocated array with per-quadrant data.
+ *                      Must be NULL if p4est->data_size == 0.
+ * \return              An array of type p4est_qcoord_t that contains
+ *                      x y level for each quadrant on this processor.
+ *                      The tree information is not extracted.
+ */
+sc_array_t         *p4est_deflate_quadrants (p4est_t * p4est,
+                                             sc_array_t ** data);
+
+/** Create a new p4est based on serialized data.
+ * See p4est.h and p4est_communication.h for more information on parameters.
+ * \param [in] mpicomm       A valid MPI communicator.
+ * \param [in] connectivity  This is the connectivity information that
+ *                           the forest is built with.  Note that p4est
+ *                           does not take ownership of the memory.
+ * \param [in] global_first_quadrant First global quadrant on each proc and
+ *                           one beyond.  Copied into global_first_quadrant.
+ *                           Local count on rank is gfq[rank + 1] - gfq[rank].
+ * \param [in] pertree       The cumulative quadrant counts per tree.
+ * \param [in] quadrants     Array as returned by p4est_deflate_quadrants.
+ * \param [in] data          Array as from p4est_deflate_quadrants or NULL.
+ *                           The elem_size of this array informs data_size.
+ *                           Its elem_count equals the number of local quads.
+ * \param [in] user_pointer  Assign to the user_pointer member of the p4est.
+ * \return              The newly created p4est.
+ */
+p4est_t            *p4est_inflate (sc_MPI_Comm mpicomm,
+                                   p4est_connectivity_t * connectivity,
+                                   const p4est_gloidx_t *
+                                   global_first_quadrant,
+                                   const p4est_gloidx_t * pertree,
+                                   sc_array_t * quadrants, sc_array_t * data,
+                                   void *user_pointer);
+
+#endif /* !P4EST_IO_H */
diff --git a/src/p4est_iterate.c b/src/p4est_iterate.c
new file mode 100644
index 0000000..f26e48c
--- /dev/null
+++ b/src/p4est_iterate.c
@@ -0,0 +1,3309 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef P4_TO_P8
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_iterate.h>
+#include <p8est_search.h>
+#else
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_iterate.h>
+#include <p4est_search.h>
+#endif
+
+/* tier ring functions:
+ *
+ * A tier saves the indices created  by an array split.  In all of the iterate
+ * loops, those indices are uniquely determined by the level of the split and
+ * the first quadrant in the section that's split.  For each level, there is a
+ * set number of tiers saved in a ring.  To get the indices from a split,
+ * we search the ring of the level of the split for the same first quadrant: if
+ * it is found, the indices are copied from the saved tier; otherwise, they are
+ * computed, and saved in the ring in place of the oldest tier in the ring */
+#define P4EST_ITER_STRIDE (P4EST_CHILDREN + 1)
+typedef struct p4est_iter_tier
+{
+  p4est_quadrant_t   *key;
+  size_t              array[P4EST_ITER_STRIDE];
+}
+p4est_iter_tier_t;
+
+typedef struct p4est_iter_tier_ring
+{
+  int                 next;
+  sc_array_t          tiers;
+}
+p4est_iter_tier_ring_t;
+
+static sc_array_t  *
+p4est_iter_tier_rings_new (int num_procs)
+{
+  int                 i, j;
+  int                 tier_ring_max;
+  int                 tier_level_max;
+  sc_array_t         *tier_rings;
+  p4est_iter_tier_ring_t *ring;
+  p4est_iter_tier_t  *tier;
+
+  tier_rings = sc_array_new (sizeof (p4est_iter_tier_ring_t));
+  tier_ring_max = (num_procs == 1 ? P4EST_CHILDREN : 2 * P4EST_CHILDREN);
+  tier_level_max = P4EST_QMAXLEVEL;
+  sc_array_resize (tier_rings, (size_t) tier_level_max);
+  for (i = 0; i < tier_level_max; i++) {
+    ring = (p4est_iter_tier_ring_t *) sc_array_index_int (tier_rings, i);
+    ring->next = 0;
+    sc_array_init (&(ring->tiers), sizeof (p4est_iter_tier_t));
+    sc_array_resize (&(ring->tiers), (size_t) tier_ring_max);
+    for (j = 0; j < tier_ring_max; j++) {
+      tier = (p4est_iter_tier_t *) sc_array_index_int (&(ring->tiers), j);
+      tier->key = NULL;
+    }
+  }
+
+  return tier_rings;
+}
+
+static void
+p4est_iter_tier_rings_destroy (sc_array_t * tier_rings)
+{
+  size_t              zz;
+  p4est_iter_tier_ring_t *ring;
+
+  for (zz = 0; zz < tier_rings->elem_count; zz++) {
+    ring = (p4est_iter_tier_ring_t *) sc_array_index (tier_rings, zz);
+    sc_array_reset (&(ring->tiers));
+  }
+  sc_array_destroy (tier_rings);
+}
+
+static void
+p4est_iter_tier_update (sc_array_t * view, int level, size_t * next_tier,
+                        size_t shift)
+{
+  int                 i;
+  p4est_split_array (view, level, next_tier);
+  for (i = 0; i < P4EST_ITER_STRIDE; i++) {
+    next_tier[i] += shift;
+  }
+}
+
+static void
+p4est_iter_tier_insert (sc_array_t * view, int level, size_t * next_tier,
+                        size_t shift, sc_array_t * tier_rings,
+                        p4est_quadrant_t * q)
+{
+  int                 i, limit;
+  p4est_iter_tier_ring_t *ring;
+  p4est_iter_tier_t  *tier;
+  p4est_quadrant_t   *key;
+
+  if (q == NULL) {
+    for (i = 0; i < P4EST_ITER_STRIDE; i++) {
+      next_tier[i] = shift;
+    }
+    return;
+  }
+
+  if (level >= (int) tier_rings->elem_count) {
+    p4est_iter_tier_update (view, level, next_tier, shift);
+    return;
+  }
+  ring = (p4est_iter_tier_ring_t *) sc_array_index_int (tier_rings, level);
+
+  limit = (int) ring->tiers.elem_count;
+  for (i = 0; i < limit; i++) {
+    tier = (p4est_iter_tier_t *) sc_array_index_int (&(ring->tiers), i);
+    key = tier->key;
+    if (key == NULL) {
+      P4EST_ASSERT (ring->next == i);
+      p4est_iter_tier_update (view, level, next_tier, shift);
+      memcpy (tier->array, next_tier, P4EST_ITER_STRIDE * sizeof (size_t));
+      tier->key = q;
+      ring->next++;
+      ring->next %= limit;
+      return;
+    }
+    if (q == key) {
+      memcpy (next_tier, tier->array, P4EST_ITER_STRIDE * sizeof (size_t));
+      return;
+    }
+  }
+
+  /* if the tier wasn't already computed, compute it */
+  p4est_iter_tier_update (view, level, next_tier, shift);
+  /* we always rewrite over the oldest created tier */
+  tier = (p4est_iter_tier_t *) sc_array_index_int (&(ring->tiers),
+                                                   ring->next++);
+  memcpy (tier->array, next_tier, P4EST_ITER_STRIDE * sizeof (size_t));
+  tier->key = q;
+  ring->next %= limit;
+}
+
+/* loop arg functions */
+typedef struct p4est_iter_loop_args
+{
+  int                 alloc_size;       /* large enough to accomodate strange
+                                           corners/edges between trees */
+#ifdef P4_TO_P8
+  int8_t              loop_edge;        /* should edge_iterate be run */
+#endif
+  int8_t              loop_corner;      /* should corner_iterate by run */
+
+  int                 level;
+  int                *level_num;        /* an array that keeps track of which
+                                           branch we take at each step in the
+                                           heirarchical search areas */
+  int                *quad_idx2;        /* an indexing variable used in
+                                           the iterate functions: passed as an
+                                           argument to avoid using alloc/free
+                                           on each call */
+  sc_array_t        **quadrants;        /* the arrays, two for each side (one
+                                           local, one ghost), that contain the
+                                           quadrants in each search area */
+  size_t            **index;    /* for each sidetype, the indices in quadrants
+                                   that form the bounds of the heirarchical
+                                   search areas */
+  size_t             *first_index;      /* an indexing variable used in the
+                                           iterate functions: passed as an
+                                           argument to avoid using alloc/free
+                                           on each call */
+  size_t             *count;    /* a counting variable used in the iterate
+                                   functions: passed as an argument to
+                                   avoid using alloc/free on each call */
+  p4est_quadrant_t  **test;     /* a testing variable used in the iterate
+                                   functions: passed as an argument to
+                                   avoid using alloc/free on each call */
+  int                *test_level;       /* a testing variable used in
+                                           the iterate functions:: passed as an
+                                           argument to avoid using alloc/free
+                                           on each call */
+  int8_t             *refine;   /* a testing variable used in the iterate
+                                   functions: passed as an argument to avoid
+                                   using alloc/free on each call */
+  sc_array_t         *tier_rings;
+}
+p4est_iter_loop_args_t;
+
+static p4est_iter_loop_args_t *
+p4est_iter_loop_args_new (p4est_connectivity_t * conn,
+#ifdef P4_TO_P8
+                          p8est_iter_edge_t iter_edge,
+#endif
+                          p4est_iter_corner_t iter_corner,
+                          p4est_ghost_t * ghost_layer, int num_procs)
+{
+  int                 i;
+  p4est_topidx_t      c;
+  int                 alloc_size;
+  p4est_topidx_t      num_corners = conn->num_corners;
+  const p4est_topidx_t *ctt_offset = conn->ctt_offset;
+#ifdef P4_TO_P8
+  p4est_topidx_t      e;
+  int                 max_edge_size;
+  int                 edge_size;
+  const p4est_topidx_t *ett_offset = conn->ett_offset;
+  p4est_topidx_t      num_edges = conn->num_edges;
+#endif
+  int                 max_corner_size;
+  int                 corner_size;
+  p4est_iter_loop_args_t *loop_args;
+
+  loop_args = P4EST_ALLOC (p4est_iter_loop_args_t, 1);
+
+  /** alloc_size is the number of index arrays that are needed in the program.
+   * at minimum we need two for each side of the face iterator: one for local,
+   * one for ghost */
+  alloc_size = 4;
+  /** in the absence of strange corners (or strange edges), P4EST_CHILDREN is
+   * the most quadrants that can meet at a corner */
+  max_corner_size = P4EST_CHILDREN;
+#ifdef P4_TO_P8
+  /** if there are no strange edges between trees, then at most 4 quadrants
+   * meet at an edge */
+  max_edge_size = 4;
+  if (iter_edge != NULL || iter_corner != NULL) {
+    for (e = 0; e < num_edges; e++) {
+      edge_size = (int) (ett_offset[e + 1] - ett_offset[e]);
+      max_edge_size = (edge_size > max_edge_size) ? edge_size : max_edge_size;
+    }
+    /** we need to have two index arrays for every side of the edge iterator:
+     * one for local, one for ghost */
+    alloc_size = (2 * max_edge_size > alloc_size) ?
+      2 * max_edge_size : alloc_size;
+    /** even if there are no strange corners, for a corner that is in the
+     * middle of a strange edge, there will be two quadrants that meet at the
+     * corner for every quadrant that meets at the edge */
+    max_corner_size = (max_edge_size * 2 > max_corner_size) ?
+      max_edge_size * 2 : max_corner_size;
+  }
+#endif
+
+  if (iter_corner != NULL) {
+    for (c = 0; c < num_corners; c++) {
+      corner_size = (int) (ctt_offset[c + 1] - ctt_offset[c]);
+      max_corner_size = (corner_size > max_corner_size) ? corner_size :
+        max_corner_size;
+    }
+    /** Similar to edges, we need to arrays for every quadrant that meets at a
+     * corner */
+    alloc_size = (2 * max_corner_size > alloc_size) ?
+      2 * max_corner_size : alloc_size;
+  }
+
+  /** initialize arrays that keep track of where we are in the search */
+  loop_args->alloc_size = alloc_size;
+  loop_args->level_num = P4EST_ALLOC (int, (P4EST_QMAXLEVEL + 1));
+  loop_args->quad_idx2 = P4EST_ALLOC (int, alloc_size / 2);
+  loop_args->quadrants = P4EST_ALLOC (sc_array_t *, alloc_size);
+  loop_args->index = P4EST_ALLOC (size_t *, alloc_size);
+  for (i = 0; i < alloc_size; i++) {
+    loop_args->index[i] =
+      P4EST_ALLOC (size_t, (P4EST_QMAXLEVEL + 1) * P4EST_ITER_STRIDE);
+    if (i & 1) {
+      loop_args->quadrants[i] = &(ghost_layer->ghosts);
+    }
+  }
+  loop_args->first_index = P4EST_ALLOC (size_t, alloc_size);
+  loop_args->count = P4EST_ALLOC (size_t, alloc_size);
+  loop_args->test = P4EST_ALLOC (p4est_quadrant_t *, alloc_size);
+  loop_args->test_level = P4EST_ALLOC (int, alloc_size);
+  loop_args->refine = P4EST_ALLOC (int8_t, alloc_size / 2);
+
+  loop_args->tier_rings = p4est_iter_tier_rings_new (num_procs);
+
+#ifdef P4_TO_P8
+  loop_args->loop_edge = ((iter_corner != NULL) || (iter_edge != NULL));
+#endif
+  loop_args->loop_corner = (iter_corner != NULL);
+
+  return loop_args;
+}
+
+static void
+p4est_iter_loop_args_destroy (p4est_iter_loop_args_t * loop_args)
+{
+  int                 i;
+  int                 alloc_size = loop_args->alloc_size;
+
+  P4EST_FREE (loop_args->level_num);
+  P4EST_FREE (loop_args->quad_idx2);
+  P4EST_FREE (loop_args->quadrants);
+  for (i = 0; i < alloc_size; i++) {
+    P4EST_FREE (loop_args->index[i]);
+  }
+  P4EST_FREE (loop_args->index);
+  P4EST_FREE (loop_args->first_index);
+  P4EST_FREE (loop_args->count);
+  P4EST_FREE (loop_args->test);
+  P4EST_FREE (loop_args->test_level);
+  P4EST_FREE (loop_args->refine);
+  p4est_iter_tier_rings_destroy (loop_args->tier_rings);
+  P4EST_FREE (loop_args);
+}
+
+/* initialize loop_args for a volume search */
+static void
+p4est_iter_init_loop_volume (p4est_iter_loop_args_t * loop_args,
+                             p4est_topidx_t t, p4est_t * p4est,
+                             p4est_ghost_t * ghost_layer)
+{
+  const int           left = 0;
+  const int           right = 1;
+  const int           local = 0;
+  const int           ghost = 1;
+  int                 i;
+  sc_array_t         *trees = p4est->trees;
+  p4est_tree_t       *tree;
+  sc_array_t         *local_quads;
+  size_t              first_ghost_quad =
+    (size_t) ghost_layer->tree_offsets[t];
+  size_t              stop_ghost_quad =
+    (size_t) ghost_layer->tree_offsets[t + 1];
+
+  tree = p4est_tree_array_index (trees, t);
+  local_quads = &(tree->quadrants);
+
+  loop_args->level = 0;
+  loop_args->level_num[0] = 0;
+
+  for (i = left; i <= right; i++) {
+    loop_args->index[i * 2 + local][0] = 0;
+    loop_args->index[i * 2 + local][1] = local_quads->elem_count;
+    loop_args->index[i * 2 + ghost][0] = first_ghost_quad;
+    loop_args->index[i * 2 + ghost][1] = stop_ghost_quad;
+  }
+  for (i = 0; i < 4; i++) {
+    loop_args->quadrants[i] = (i & 1) ? &(ghost_layer->ghosts) : local_quads;
+  }
+#ifdef P4_TO_P8
+  if (loop_args->loop_edge) {
+    for (; i < 8; i++) {
+      loop_args->quadrants[i] =
+        (i & 1) ? &(ghost_layer->ghosts) : local_quads;
+    }
+  }
+#endif
+  if (loop_args->loop_corner) {
+    for (; i < 2 * P4EST_CHILDREN; i++) {
+      loop_args->quadrants[i] =
+        (i & 1) ? &(ghost_layer->ghosts) : local_quads;
+    }
+  }
+}
+
+/* initialize loop_args for a face search between trees */
+static void
+p4est_iter_init_loop_face (p4est_iter_loop_args_t * loop_args,
+                           p4est_topidx_t t, p4est_topidx_t nt,
+                           p4est_t * p4est, p4est_ghost_t * ghost_layer)
+{
+  const int           left = 0;
+  const int           right = 1;
+  const int           local = 0;
+  const int           ghost = 1;
+  int                 i;
+  sc_array_t         *trees = p4est->trees;
+  p4est_tree_t       *tree;
+  sc_array_t         *left_local_quads;
+  sc_array_t         *right_local_quads;
+  size_t              left_first_ghost =
+    (size_t) ghost_layer->tree_offsets[t];
+  size_t              left_stop_ghost =
+    (size_t) ghost_layer->tree_offsets[t + 1];
+  size_t              right_first_ghost =
+    (size_t) ghost_layer->tree_offsets[nt];
+  size_t              right_stop_ghost =
+    (size_t) ghost_layer->tree_offsets[nt + 1];
+
+  tree = p4est_tree_array_index (trees, t);
+  left_local_quads = &(tree->quadrants);
+  tree = p4est_tree_array_index (trees, nt);
+  right_local_quads = &(tree->quadrants);
+
+  loop_args->level = 0;
+  loop_args->level_num[0] = 0;
+
+  loop_args->index[left * 2 + local][0] = 0;
+  loop_args->index[left * 2 + local][1] = left_local_quads->elem_count;
+  loop_args->index[left * 2 + ghost][0] = left_first_ghost;
+  loop_args->index[left * 2 + ghost][1] = left_stop_ghost;
+
+  loop_args->index[right * 2 + local][0] = 0;
+  loop_args->index[right * 2 + local][1] = right_local_quads->elem_count;
+  loop_args->index[right * 2 + ghost][0] = right_first_ghost;
+  loop_args->index[right * 2 + ghost][1] = right_stop_ghost;
+
+  loop_args->quadrants[left * 2 + local] = left_local_quads;
+  loop_args->quadrants[left * 2 + ghost] = &(ghost_layer->ghosts);
+  loop_args->quadrants[right * 2 + local] = right_local_quads;
+  loop_args->quadrants[right * 2 + ghost] = &(ghost_layer->ghosts);
+  i = 4;
+#ifdef P4_TO_P8
+  if (loop_args->loop_edge) {
+    for (; i < 8; i++) {
+      loop_args->quadrants[i] = (i & 1) ? &(ghost_layer->ghosts) :
+        ((i >> 1) & 1) ? right_local_quads : left_local_quads;
+    }
+  }
+#endif
+  if (loop_args->loop_corner) {
+    for (; i < 2 * P4EST_CHILDREN; i++) {
+      loop_args->quadrants[i] = (i & 1) ? &(ghost_layer->ghosts) :
+        ((i >> 1) & 1) ? right_local_quads : left_local_quads;
+    }
+  }
+}
+
+/* initialize loop_args for a face search on a boundary face */
+static void
+p4est_iter_init_loop_outside_face (p4est_iter_loop_args_t * loop_args,
+                                   p4est_topidx_t t, p4est_t * p4est,
+                                   p4est_ghost_t * ghost_layer)
+{
+  const int           local = 0;
+  const int           ghost = 1;
+  int                 i;
+  sc_array_t         *trees = p4est->trees;
+  p4est_tree_t       *tree;
+  sc_array_t         *local_quads;
+  size_t              first_ghost_quad =
+    (size_t) ghost_layer->tree_offsets[t];
+  size_t              stop_ghost_quad =
+    (size_t) ghost_layer->tree_offsets[t + 1];
+
+  tree = p4est_tree_array_index (trees, t);
+  local_quads = &(tree->quadrants);
+
+  loop_args->level = 0;
+  loop_args->level_num[0] = 0;
+
+  loop_args->index[local][0] = 0;
+  loop_args->index[local][1] = local_quads->elem_count;
+  loop_args->index[ghost][0] = first_ghost_quad;
+  loop_args->index[ghost][1] = stop_ghost_quad;
+
+  loop_args->quadrants[local] = local_quads;
+  loop_args->quadrants[ghost] = &(ghost_layer->ghosts);
+  i = 2;
+#ifdef P4_TO_P8
+  if (loop_args->loop_edge) {
+    for (; i < 4; i++) {
+      loop_args->quadrants[i] =
+        (i & 1) ? &(ghost_layer->ghosts) : local_quads;
+    }
+  }
+#endif
+  if (loop_args->loop_corner) {
+    for (; i < P4EST_CHILDREN; i++) {
+      loop_args->quadrants[i] =
+        (i & 1) ? &(ghost_layer->ghosts) : local_quads;
+    }
+  }
+}
+
+/* initialize loop_args for an edge search between trees */
+#ifdef P4_TO_P8
+static void
+p8est_iter_init_loop_edge (p4est_iter_loop_args_t * loop_args,
+                           p8est_t * p8est, p4est_ghost_t * ghost_layer,
+                           p8est_iter_edge_info_t * info)
+{
+  const int           local = 0;
+  const int           ghost = 1;
+
+  size_t              zz;
+  size_t              limit = info->sides.elem_count;
+  p8est_iter_edge_side_t *side;
+  p4est_topidx_t      t;
+  sc_array_t         *trees = p8est->trees;
+  p8est_tree_t       *tree;
+  sc_array_t         *local_quads;
+
+  loop_args->level = 0;
+  loop_args->level_num[0] = 0;
+
+  for (zz = 0; zz < limit; zz++) {
+    side = p8est_iter_eside_array_index (&info->sides, zz);
+    t = side->treeid;
+    tree = p4est_tree_array_index (trees, t);
+    local_quads = &(tree->quadrants);
+    loop_args->index[zz * 2 + local][0] = 0;
+    loop_args->index[zz * 2 + local][1] = local_quads->elem_count;
+    loop_args->index[zz * 2 + ghost][0] =
+      (size_t) ghost_layer->tree_offsets[t];
+    loop_args->index[zz * 2 + ghost][1] =
+      (size_t) ghost_layer->tree_offsets[t + 1];
+    loop_args->quadrants[zz * 2 + local] = local_quads;
+    loop_args->quadrants[zz * 2 + ghost] = &(ghost_layer->ghosts);
+    if (loop_args->loop_corner) {
+      loop_args->quadrants[(limit + zz) * 2 + local] = local_quads;
+      loop_args->quadrants[(limit + zz) * 2 + ghost] = &(ghost_layer->ghosts);
+    }
+  }
+}
+#endif
+
+/* initialize loop_args for a corner search between trees */
+static void
+p4est_iter_init_loop_corner (p4est_iter_loop_args_t * loop_args,
+                             p4est_t * p4est, p4est_ghost_t * ghost_layer,
+                             p4est_iter_corner_info_t * info)
+{
+  const int           local = 0;
+  const int           ghost = 1;
+
+  size_t              zz;
+  size_t              limit = info->sides.elem_count;
+  p4est_iter_corner_side_t *side;
+  p4est_topidx_t      t;
+  sc_array_t         *trees = p4est->trees;
+  p4est_tree_t       *tree;
+  sc_array_t         *local_quads;
+
+  loop_args->level = 0;
+  loop_args->level_num[0] = 0;
+
+  for (zz = 0; zz < limit; zz++) {
+    side = p4est_iter_cside_array_index (&info->sides, zz);
+    t = side->treeid;
+    tree = p4est_tree_array_index (trees, t);
+    local_quads = &(tree->quadrants);
+    loop_args->index[zz * 2 + local][0] = 0;
+    loop_args->index[zz * 2 + local][1] = local_quads->elem_count;
+    loop_args->index[zz * 2 + ghost][0] =
+      (size_t) ghost_layer->tree_offsets[t];
+    loop_args->index[zz * 2 + ghost][1] =
+      (size_t) ghost_layer->tree_offsets[t + 1];
+    loop_args->quadrants[zz * 2 + local] = local_quads;
+    loop_args->quadrants[zz * 2 + ghost] = &(ghost_layer->ghosts);
+  }
+}
+
+/* When one iterate loop calls another, e.g. volume_iterate calls face_iterate,
+ * the initial bounds for the new sides of the search need to be initialized.
+ * The whole heirarchy doesn't need to be copied, just the most recent bounds
+ * from the correct starting sections (start_idx2).
+ */
+static void
+p4est_iter_copy_indices (p4est_iter_loop_args_t * loop_args,
+                         const int *start_idx2, int old_num, int factor)
+{
+  const int           local = 0;
+  const int           ghost = 1;
+  int                 r, s, t;
+  size_t            **zindex = loop_args->index;
+  int                 i, j, idx2 = loop_args->level * P4EST_ITER_STRIDE;
+
+  for (r = old_num, j = 1; j < factor; j++) {
+    for (s = 0; s < old_num; s++, r++) {
+      i = idx2 + start_idx2[r];
+      for (t = local; t <= ghost; t++) {
+        zindex[r * 2 + t][i] = zindex[s * 2 + t][i];
+        zindex[r * 2 + t][i + 1] = zindex[s * 2 + t][i + 1];
+      }
+    }
+  }
+}
+
+/* corner iterate function */
+typedef struct p4est_iter_corner_args
+{
+  int                 num_sides;
+  int                *start_idx2;
+  int                 remote;
+  p4est_iter_loop_args_t *loop_args;
+  p4est_iter_corner_info_t info;
+}
+p4est_iter_corner_args_t;
+
+static int
+cside_compare (const void *a, const void *b)
+{
+  p4est_locidx_t      diff;
+  const p4est_iter_corner_side_t *A = (const p4est_iter_corner_side_t *) a;
+  const p4est_iter_corner_side_t *B = (const p4est_iter_corner_side_t *) b;
+
+  diff = (A->treeid - B->treeid);
+  if (diff) {
+    return (int) diff;
+  }
+  return (A->corner - B->corner);
+}
+
+/* initialize corner args for a corner between trees */
+static void
+p4est_iter_init_corner (p4est_iter_corner_args_t * args,
+                        p4est_t * p4est, p4est_ghost_t * ghost_layer,
+                        p4est_iter_loop_args_t * loop_args, p4est_topidx_t t,
+                        int c)
+{
+  p4est_topidx_t      ti;
+  int                 i, j, k;
+  int                 f, nf, o;
+  int                 c2, nc;
+  int                 count = 0;
+  p4est_topidx_t      nt;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  p4est_topidx_t     *ttt = conn->tree_to_tree;
+  int8_t             *ttf = conn->tree_to_face;
+  p4est_topidx_t     *ttc = conn->tree_to_corner;
+  p4est_topidx_t     *ctt_offset = conn->ctt_offset;
+  p4est_topidx_t     *ctt = conn->corner_to_tree;
+  int8_t             *ctc = conn->corner_to_corner;
+  p4est_topidx_t      corner = ttc != NULL ? ttc[t * P4EST_CHILDREN + c] : -1;
+#ifdef P4_TO_P8
+  int                 ref, set, nc2, orig_o;
+  int                 e, ne, l;
+  p4est_topidx_t     *tte = conn->tree_to_edge;
+  p4est_topidx_t     *ett_offset = conn->ett_offset;
+  p4est_topidx_t     *ett = conn->edge_to_tree;
+  int8_t             *ete = conn->edge_to_edge;
+  p4est_topidx_t      edge;
+#endif
+  p4est_iter_corner_info_t *info = &(args->info);
+  p4est_iter_corner_side_t *cside;
+  int                *start_idx2;
+
+  info->p4est = p4est;
+  info->ghost_layer = ghost_layer;
+  info->tree_boundary = (int8_t) P4EST_CONNECT_CORNER;
+  sc_array_init (&(info->sides), sizeof (p4est_iter_corner_side_t));
+  start_idx2 = args->start_idx2 =
+    P4EST_ALLOC (int, loop_args->alloc_size / 2);
+  args->loop_args = loop_args;
+
+  if (corner >= 0) {
+    for (ti = ctt_offset[corner]; ti < ctt_offset[corner + 1]; ti++) {
+      nt = ctt[ti];
+      nc = (int) ctc[ti];
+      cside = (p4est_iter_corner_side_t *) sc_array_push (&(info->sides));
+      cside->corner = (int8_t) nc;
+      cside->treeid = nt;
+      start_idx2[count++] = 0;
+      for (j = 0; j < P4EST_DIM; j++) {
+        cside->faces[j] = -1;
+#ifdef P4_TO_P8
+        cside->edges[j] = -1;
+#endif
+      }
+    }
+  }
+  else {
+    cside = (p4est_iter_corner_side_t *) sc_array_push (&(info->sides));
+    cside->corner = (int8_t) c;
+    cside->treeid = t;
+    for (j = 0; j < P4EST_DIM; j++) {
+      cside->faces[j] = -1;
+#ifdef P4_TO_P8
+      cside->edges[j] = -1;
+#endif
+    }
+    start_idx2[count++] = 0;
+    for (i = 0; i < P4EST_DIM; i++) {
+      f = p4est_corner_faces[c][i];
+      c2 = p4est_corner_face_corners[c][f];
+      nt = ttt[t * P4EST_FACES + f];
+      nf = (int) ttf[t * P4EST_FACES + f];
+      o = nf / P4EST_FACES;
+      nf %= P4EST_FACES;
+      if (nt == t && nf == f) {
+        continue;
+      }
+#ifndef P4_TO_P8
+      nc = p4est_face_corners[nf][(o == 0) ? c2 : (1 - c2)];
+#else
+      ref = p8est_face_permutation_refs[f][nf];
+      set = p8est_face_permutation_sets[ref][o];
+      nc2 = p8est_face_permutations[set][c2];
+      nc = p8est_face_corners[nf][nc2];
+#endif
+      for (j = 0; j < count; j++) {
+        cside = p4est_iter_cside_array_index_int (&(info->sides), j);
+        if (cside->treeid == nt && (int) cside->corner == nc) {
+          break;
+        }
+      }
+      if (j < count) {
+        continue;
+      }
+      cside = (p4est_iter_corner_side_t *) sc_array_push (&(info->sides));
+      cside->corner = (int8_t) nc;
+      cside->treeid = nt;
+      for (j = 0; j < P4EST_DIM; j++) {
+        cside->faces[j] = -1;
+#ifdef P4_TO_P8
+        cside->edges[j] = -1;
+#endif
+      }
+      start_idx2[count++] = 0;
+    }
+#ifdef P4_TO_P8
+    for (i = 0; i < 3; i++) {
+      e = p8est_corner_edges[c][i];
+      c2 = (p8est_edge_corners[e][0] == c) ? 0 : 1;
+      edge = (tte != NULL) ? tte[t * 12 + e] : -1;
+      if (edge >= 0) {
+        orig_o = -1;
+        for (ti = ett_offset[edge]; ti < ett_offset[edge + 1]; ti++) {
+          nt = ett[ti];
+          ne = (int) ete[ti];
+          o = ne / 12;
+          ne %= 12;
+          if (nt == t && ne == e) {
+            orig_o = o;
+            break;
+          }
+        }
+        P4EST_ASSERT (orig_o >= 0);
+        P4EST_ASSERT (ti < ett_offset[edge + 1]);
+        for (ti = ett_offset[edge]; ti < ett_offset[edge + 1]; ti++) {
+          nt = ett[ti];
+          ne = (int) ete[ti];
+          o = ne / 12;
+          ne %= 12;
+          if (nt == t && ne == e) {
+            continue;
+          }
+          nc = p8est_edge_corners[ne][(o == orig_o) ? c2 : (1 - c2)];
+          for (j = 0; j < count; j++) {
+            cside = p4est_iter_cside_array_index_int (&(info->sides), j);
+            if (cside->treeid == nt && (int) cside->corner == nc) {
+              break;
+            }
+          }
+          if (j < count) {
+            continue;
+          }
+          cside = (p4est_iter_corner_side_t *) sc_array_push (&(info->sides));
+          cside->corner = (int8_t) nc;
+          cside->treeid = nt;
+          for (j = 0; j < P4EST_DIM; j++) {
+            cside->faces[j] = -1;
+#ifdef P4_TO_P8
+            cside->edges[j] = -1;
+#endif
+          }
+          start_idx2[count++] = 0;
+        }
+      }
+    }
+#endif
+  }
+
+  sc_array_sort (&(info->sides), cside_compare);
+
+  {
+    int                 faces_count = 0;
+#ifdef P4_TO_P8
+    int                 edges_count = 0;
+#endif
+    for (i = 0; i < count; i++) {
+      cside = p4est_iter_cside_array_index_int (&(info->sides), i);
+      nt = cside->treeid;
+      nc = cside->corner;
+      for (j = 0; j < P4EST_DIM; j++) {
+        if (cside->faces[j] == -1) {
+          int                 nnc;
+          p4est_topidx_t      nnt;
+          p4est_iter_corner_side_t *cside2;
+
+          cside->faces[j] = faces_count;
+
+          f = p4est_corner_faces[nc][j];
+          nnt = ttt[P4EST_FACES * nt + f];
+          c2 = p4est_corner_face_corners[nc][f];
+          nf = (int) ttf[P4EST_FACES * nt + f];
+          o = nf / P4EST_FACES;
+
+          nf %= P4EST_FACES;
+          if (nnt == nt && nf == f) {
+            faces_count++;
+            continue;
+          }
+
+#ifndef P4_TO_P8
+          nnc = p4est_face_corners[nf][(o == 0) ? c2 : (1 - c2)];
+#else
+          ref = p8est_face_permutation_refs[f][nf];
+          set = p8est_face_permutation_sets[ref][o];
+          nc2 = p8est_face_permutations[set][c2];
+          nnc = p8est_face_corners[nf][nc2];
+#endif
+
+          for (k = 0; k < count; k++) {
+            if (k == i) {
+              continue;
+            }
+            cside2 = p4est_iter_cside_array_index_int (&(info->sides), k);
+            if (cside2->treeid == nnt && (int) cside2->corner == nnc) {
+              cside2->faces[nf / 2] = faces_count;
+            }
+          }
+          faces_count++;
+        }
+#ifdef P4_TO_P8
+        if (cside->edges[j] == -1) {
+          p4est_topidx_t      nnt;
+
+          cside->edges[j] = edges_count;
+          e = p8est_corner_edges[nc][j];
+          c2 = (p8est_edge_corners[e][0] == nc) ? 0 : 1;
+          edge = (tte != NULL) ? tte[nt * 12 + e] : -1;
+          if (edge >= 0) {
+            orig_o = -1;
+            for (ti = ett_offset[edge]; ti < ett_offset[edge + 1]; ti++) {
+              nnt = ett[ti];
+              ne = (int) ete[ti];
+              o = ne / 12;
+              ne %= 12;
+              if (nnt == nt && ne == e) {
+                orig_o = o;
+                break;
+              }
+            }
+            P4EST_ASSERT (orig_o >= 0);
+            P4EST_ASSERT (ti < ett_offset[edge + 1]);
+            for (ti = ett_offset[edge]; ti < ett_offset[edge + 1]; ti++) {
+              int                 nnc;
+
+              nnt = ett[ti];
+              ne = (int) ete[ti];
+              o = ne / 12;
+              ne %= 12;
+              if (nnt == nt && ne == e) {
+                continue;
+              }
+              nnc = p8est_edge_corners[ne][(o == orig_o) ? c2 : (1 - c2)];
+              for (k = 0; k < count; k++) {
+                p4est_iter_corner_side_t *cside2;
+
+                cside2 = p4est_iter_cside_array_index_int (&(info->sides), k);
+                if (cside2->treeid == nnt && (int) cside2->corner == nnc) {
+                  cside2->edges[ne / 4] = edges_count;
+                  break;
+                }
+              }
+              P4EST_ASSERT (k < count);
+            }
+          }
+          else {
+            for (l = 0; l < 2; l++) {
+              int                 nnc, e2;
+              p4est_iter_corner_side_t *cside2;
+
+              f = p8est_edge_faces[e][l];
+              nnt = ttt[P4EST_FACES * nt + f];
+              c2 = p4est_corner_face_corners[nc][f];
+              nf = (int) ttf[P4EST_FACES * nt + f];
+              o = nf / P4EST_FACES;
+
+              nf %= P4EST_FACES;
+              if (nnt == nt && nf == f) {
+                continue;
+              }
+              ref = p8est_face_permutation_refs[f][nf];
+              set = p8est_face_permutation_sets[ref][o];
+              nc2 = p8est_face_permutations[set][c2];
+              nnc = p8est_face_corners[nf][nc2];
+              c2 = p8est_edge_corners[e][0] == nc ? p8est_edge_corners[e][1] :
+                p8est_edge_corners[e][0];
+              c2 = p8est_corner_face_corners[c2][f];
+              nc2 = p8est_face_permutations[set][c2];
+              nc2 = p8est_face_corners[nf][nc2];
+              e2 = p8est_child_corner_edges[nnc][nc2];
+              P4EST_ASSERT (e2 >= 0);
+              for (k = 0; k < count; k++) {
+                if (k == i) {
+                  continue;
+                }
+                cside2 = p4est_iter_cside_array_index_int (&(info->sides), k);
+                if (cside2->treeid == nnt && (int) cside2->corner == nnc) {
+                  cside2->edges[e2 / 4] = edges_count;
+
+                }
+              }
+            }
+          }
+          edges_count++;
+        }
+#endif
+      }
+    }
+  }
+
+  args->num_sides = count;
+  p4est_iter_init_loop_corner (loop_args, p4est, ghost_layer, info);
+}
+
+static void
+p4est_iter_reset_corner (p4est_iter_corner_args_t * args)
+{
+  sc_array_reset (&(args->info.sides));
+  P4EST_FREE (args->start_idx2);
+}
+
+static void
+p4est_corner_iterate (p4est_iter_corner_args_t * args, void *user_data,
+                      p4est_iter_corner_t iter_corner)
+{
+  const int           local = 0;
+  const int           ghost = 1;
+
+  int                 side, st;
+
+  p4est_iter_loop_args_t *loop_args = args->loop_args;
+  int                 level = loop_args->level;
+  int                 num_sides = args->num_sides;
+  const int          *start_idx2 = args->start_idx2;
+  int                *quad_idx2 = loop_args->quad_idx2;
+  int                 this_corner;
+  sc_array_t        **quadrants = loop_args->quadrants;
+  size_t            **zindex = loop_args->index;
+  size_t             *first_index = loop_args->first_index;
+  size_t             *count = loop_args->count;
+  p4est_quadrant_t  **test = loop_args->test;
+  p4est_quadrant_t    temp;
+  p4est_qcoord_t      mask =
+    ((p4est_qcoord_t) - 1) << (P4EST_MAXLEVEL - level);
+  sc_array_t          test_view;
+  p4est_iter_corner_info_t *info = &(args->info);
+  p4est_iter_corner_side_t *cside;
+  ssize_t             temp_idx;
+  int                 level_idx2;
+  int                 type;
+  int8_t              has_local;
+  int                 lmax = 0;
+
+  /* level_idx2 moves us to the correct set of bounds within the index arrays
+   * for the level: it is a set of bounds because it includes all children at
+   * this level */
+  level_idx2 = level * P4EST_ITER_STRIDE;
+
+  for (side = 0; side < num_sides; side++) {
+    /* start_idx2 gives the ancestor id at level for the search area on this
+     * side, so quad_idx2[side] now gives the correct location in
+     * index[sidetype] of the bounds of the search area */
+    quad_idx2[side] = level_idx2 + start_idx2[side];
+
+    /* get the location in quadrants[sidetype] of the first quadrant in the
+     * search area, and the count of quadrants in the search area, and
+     * initialize tests to NULL */
+    for (type = local; type <= ghost; type++) {
+      st = side * 2 + type;
+      first_index[st] = zindex[st][quad_idx2[side]];
+      count[st] = (zindex[st][quad_idx2[side] + 1] - first_index[st]);
+      test[st] = NULL;
+    }
+  }
+
+  /* corner_iterate only runs if there is a chance of a local quadrant touching
+   * the desired corner */
+  for (side = 0; side < num_sides; side++) {
+    if (count[side * 2 + local]) {
+      break;
+    }
+  }
+  if (side == num_sides) {
+    return;
+  }
+
+  has_local = 0;
+  for (side = 0; side < num_sides; side++) {
+
+    cside = p4est_iter_cside_array_index_int (&info->sides, side);
+    cside->quad = NULL;
+    cside->is_ghost = 1;
+    cside->quadid = -1;
+    this_corner = (int) cside->corner;
+    for (type = local; type <= ghost; type++) {
+      st = side * 2 + type;
+      /* if we already found something locally, there's no need to search the
+       * ghost layer */
+      if (test[side * 2 + local] != NULL) {
+        continue;
+      }
+
+      /* for this sidetype, we must find the most likely candidate in the
+       * search area for touching the desired corner */
+      if (count[st]) {
+        /* get a candidate */
+        if (count[st] == 1) {
+          test[st] = p4est_quadrant_array_index (quadrants[st],
+                                                 first_index[st]);
+          temp_idx = 0;
+        }
+        else {
+          switch (this_corner) {
+          case (P4EST_CHILDREN - 1):
+            test[st] = p4est_quadrant_array_index (quadrants[st],
+                                                   first_index[st] +
+                                                   count[st] - 1);
+            temp_idx = ((ssize_t) count[st]) - 1;
+            break;
+          default:
+            P4EST_ASSERT (first_index[st] < quadrants[st]->elem_count);
+            test[st] = p4est_quadrant_array_index (quadrants[st],
+                                                   first_index[st]);
+            temp_idx = 0;
+            break;
+          }
+        }
+        /* create the smallest quadrant in the appropriate corner */
+        temp = *(test[st]);
+        temp.level = (int8_t) level;
+        temp.x &= mask;
+        temp.y &= mask;
+#ifdef P4_TO_P8
+        temp.z &= mask;
+#endif
+        p4est_quadrant_corner_descendant (&temp, &temp, this_corner,
+                                          P4EST_QMAXLEVEL);
+        P4EST_ASSERT (p4est_quadrant_is_valid (&temp));
+        /* we do not have to search if there is one quadrant, or if we are in
+         * the first or last corner */
+        if (count[st] == 1 || this_corner == 0 ||
+            this_corner == P4EST_CHILDREN - 1) {
+          /* if test[sidetype] does not contain temp */
+          if ((!p4est_quadrant_is_equal (test[st], &temp)) &&
+              (!p4est_quadrant_is_ancestor (test[st], &temp))) {
+            test[st] = NULL;
+          }
+        }
+        else {
+          /* we search for the quadrant containing temp */
+          sc_array_init_view (&test_view, quadrants[st],
+                              first_index[st], count[st]);
+          temp_idx = sc_array_bsearch (&test_view, &temp,
+                                       p4est_quadrant_disjoint);
+          /* if there is no quadrant containing temp, then no quad in the
+           * search area can touch the corner */
+          if (temp_idx == -1) {
+            test[st] = NULL;
+          }
+          else {
+            P4EST_ASSERT (temp_idx >= 0);
+            test[st] = p4est_quadrant_array_index (&test_view,
+                                                   (size_t) temp_idx);
+          }
+        }
+        /* if we have found the right quadrant for this side of the corner */
+        if (test[st] != NULL) {
+          P4EST_ASSERT (p4est_quadrant_overlaps (test[st], &temp));
+          P4EST_ASSERT (temp_idx >= 0 && (size_t) temp_idx < count[st]);
+          temp_idx += first_index[st];
+          cside->quad = test[st];
+          cside->is_ghost = (type == ghost);
+          cside->quadid = (p4est_locidx_t) temp_idx;
+          if (type == local) {
+            has_local = 1;
+          }
+          lmax = SC_MAX (lmax, test[st]->level);
+        }
+      }
+    }
+  }
+  if (has_local) {
+    /* always run if a local quadrant touches the corner */
+    iter_corner (info, user_data);
+  }
+  else if (args->remote) {
+    /* otherwise, if are supposed to run the callback if the corner is
+     * remotely touched, we have to determine if the corner is remotely
+     * touched */
+    p4est_t            *p4est = info->p4est;
+    p4est_quadrant_t   *fq = &(p4est->global_first_position[p4est->mpirank]);
+    p4est_quadrant_t   *lq =
+      &(p4est->global_first_position[p4est->mpirank + 1]);
+    p4est_topidx_t      flt = fq->p.which_tree;
+    p4est_topidx_t      llt = lq->p.which_tree;
+
+    for (side = 0; side < num_sides; side++) {
+      cside = p4est_iter_cside_array_index_int (&info->sides, side);
+      p4est_quadrant_t   *q = cside->quad;
+      int                 dir;
+      int                 l = -1;
+
+      if (q == NULL || (l = q->level) == lmax) {
+        continue;
+      }
+      for (dir = 0; dir < P4EST_DIM; dir++) {
+        int                 f = cside->faces[dir];
+        int                 side2;
+        int                 ht;
+
+        for (side2 = 0; side2 < num_sides; side2++) {
+          p4est_iter_corner_side_t *cside2;
+          int                 dir2;
+
+          if (side2 == side) {
+            continue;
+          }
+
+          cside2 = p4est_iter_cside_array_index_int (&info->sides, side2);
+
+          ht = cside2->treeid;
+
+          for (dir2 = 0; dir2 < P4EST_DIM; dir2++) {
+            if (cside2->faces[dir2] == f && cside2->quad) {
+              p4est_quadrant_t   *q2 = cside2->quad;
+              int                 i;
+              int                 nf =
+                p4est_corner_faces[cside2->corner][dir2];
+
+              if (q2->level < l + 1) {
+                continue;
+              }
+
+              for (i = 0; i < P4EST_HALF; i++) {
+                int                 nc = p4est_face_corners[nf][i];
+                p4est_quadrant_t    h;
+                if (nc == cside2->corner) {
+                  continue;
+                }
+                p4est_quadrant_sibling (q2, &h, nc);
+                if ((ht > flt ||
+                     (ht == flt && p4est_quadrant_disjoint (fq, &h) <= 0))
+                    &&
+                    (ht < llt ||
+                     (ht == llt && p4est_quadrant_disjoint (&h, lq) < 0))) {
+                  iter_corner (info, user_data);
+                  return;
+                }
+              }
+            }
+          }
+        }
+      }
+#ifdef P4_TO_P8
+      for (dir = 0; dir < P4EST_DIM; dir++) {
+        int                 e = cside->edges[dir];
+        int                 side2;
+        int                 ht;
+
+        for (side2 = 0; side2 < num_sides; side2++) {
+          p4est_iter_corner_side_t *cside2;
+          int                 dir2;
+
+          if (side2 == side) {
+            continue;
+          }
+          cside2 = p4est_iter_cside_array_index_int (&info->sides, side2);
+          ht = cside2->treeid;
+          for (dir2 = 0; dir2 < P4EST_DIM; dir2++) {
+            if (cside2->edges[dir2] == e && cside2->quad) {
+              p4est_quadrant_t    h;
+              p4est_quadrant_t   *q2 = cside2->quad;
+              int                 ne =
+                p8est_corner_edges[cside2->corner][dir2];
+
+              if (q2->level < l + 1) {
+                continue;
+              }
+
+              if (p8est_edge_corners[ne][0] == cside2->corner) {
+                p4est_quadrant_sibling (q2, &h, p8est_edge_corners[ne][1]);
+              }
+              else {
+                p4est_quadrant_sibling (q2, &h, p8est_edge_corners[ne][0]);
+              }
+              if ((ht > flt ||
+                   (ht == flt && p4est_quadrant_disjoint (fq, &h) <= 0))
+                  &&
+                  (ht < llt ||
+                   (ht == llt && p4est_quadrant_disjoint (&h, lq) < 0))) {
+                /* run the callback */
+                iter_corner (info, user_data);
+                return;
+              }
+            }
+          }
+        }
+      }
+#endif
+    }
+  }
+}
+
+/* edge iterate functions */
+#ifdef P4_TO_P8
+typedef struct p8est_iter_edge_args
+{
+  int                 num_sides;
+  int                *start_idx2;
+  sc_array_t          common_corners[2];        /* for each side of the edge,
+                                                   there are two corners that
+                                                   touch the edge */
+  p4est_iter_loop_args_t *loop_args;
+  p4est_iter_corner_args_t corner_args;
+  p8est_iter_edge_info_t info;
+  int                 remote;
+}
+p8est_iter_edge_args_t;
+
+/* given valid edge arguments, setup the corner arguments for a corner search
+ * that is called for the corner where two adjacent, colinear edges meet */
+static void
+p8est_iter_init_corner_from_edge (p4est_iter_corner_args_t * args,
+                                  p8est_iter_edge_args_t * edge_args)
+{
+  int                 j, k, dir, ndir1, ndir2;;
+  p8est_iter_corner_info_t *info = &(args->info);
+  p8est_iter_edge_side_t *eside;
+  p8est_iter_corner_side_t *cside;
+  sc_array_t         *common_corners = edge_args->common_corners;
+  int                *c_start_idx2;
+  int                 n_edge_faces = 0;
+
+  info->p4est = edge_args->info.p4est;
+  info->ghost_layer = edge_args->info.ghost_layer;
+  info->tree_boundary = edge_args->info.tree_boundary;
+  sc_array_init (&(info->sides), sizeof (p4est_iter_corner_side_t));
+  args->loop_args = edge_args->loop_args;
+  args->num_sides = edge_args->num_sides * 2;
+  c_start_idx2 = args->start_idx2 = P4EST_ALLOC (int, args->num_sides);
+  sc_array_resize (&(info->sides), (size_t) args->num_sides);
+
+  for (j = 0; j < edge_args->num_sides; j++) {
+    eside = p8est_iter_eside_array_index_int (&edge_args->info.sides, j);
+    n_edge_faces = SC_MAX (n_edge_faces, eside->faces[0] + 1);
+    n_edge_faces = SC_MAX (n_edge_faces, eside->faces[1] + 1);
+  }
+  for (j = 0; j < args->num_sides; j++) {
+    k = j % edge_args->num_sides;
+    eside = p8est_iter_eside_array_index_int (&edge_args->info.sides, k);
+    cside = p4est_iter_cside_array_index_int (&info->sides, j);
+    cside->treeid = eside->treeid;
+    dir = eside->edge / 4;
+    cside->faces[dir] = 2 * n_edge_faces + k;
+    ndir1 = SC_MIN (((dir + 1) % 3), ((dir + 2) % 3));
+    ndir2 = SC_MAX (((dir + 1) % 3), ((dir + 2) % 3));
+    cside->edges[ndir1] = eside->faces[1];
+    cside->edges[ndir2] = eside->faces[0];
+    if (j == k) {
+      cside->corner = (int8_t) * ((int *) sc_array_index_int
+                                  (&(common_corners[1]), k));
+      c_start_idx2[j] = *((int *) sc_array_index_int (&(common_corners[0]),
+                                                      k));
+      cside->edges[dir] = n_edge_faces;
+      cside->faces[ndir1] = eside->faces[0];
+      cside->faces[ndir2] = eside->faces[1];
+    }
+    else {
+      cside->corner = (int8_t) * ((int *) sc_array_index_int
+                                  (&(common_corners[0]), k));
+      c_start_idx2[j] = *((int *) sc_array_index_int (&(common_corners[1]),
+                                                      k));
+      cside->edges[dir] = n_edge_faces + 1;
+      cside->faces[ndir1] = eside->faces[0] + n_edge_faces;
+      cside->faces[ndir2] = eside->faces[1] + n_edge_faces;
+    }
+  }
+  args->remote = edge_args->remote;
+}
+
+static int
+eside_compare (const void *a, const void *b)
+{
+  p4est_locidx_t      diff;
+  const p8est_iter_edge_side_t *A = (const p8est_iter_edge_side_t *) a;
+  const p8est_iter_edge_side_t *B = (const p8est_iter_edge_side_t *) b;
+
+  diff = (A->treeid - B->treeid);
+  if (diff) {
+    return (int) diff;
+  }
+  return (A->edge - B->edge);
+}
+
+/* initialize edge args for an edge between trees */
+static void
+p8est_iter_init_edge (p8est_iter_edge_args_t * args, p8est_t * p8est,
+                      p4est_ghost_t * ghost_layer,
+                      p4est_iter_loop_args_t * loop_args, p4est_topidx_t t,
+                      int e)
+{
+  p4est_topidx_t      ti;
+  int                 i, j, k;
+  int                 f, nf, o, ref, set;
+  int                 ne;
+  int                 c0, c1, nc0, nc1, *cc;
+  int                 count = 0;
+  p4est_topidx_t      nt;
+  p8est_connectivity_t *conn = p8est->connectivity;
+  p8est_iter_edge_info_t *info = &(args->info);
+  p8est_iter_edge_side_t *eside;
+  int                *start_idx2;
+  p4est_topidx_t     *ttt = conn->tree_to_tree;
+  int8_t             *ttf = conn->tree_to_face;
+  p4est_topidx_t     *tte = conn->tree_to_edge;
+  p4est_topidx_t     *ett_offset = conn->ett_offset;
+  p4est_topidx_t     *ett = conn->edge_to_tree;
+  int8_t             *ete = conn->edge_to_edge;
+  p4est_topidx_t      edge = (tte != NULL) ? tte[t * 12 + e] : -1;
+  sc_array_t         *common_corners = args->common_corners;
+
+  info->p4est = p8est;
+  info->ghost_layer = ghost_layer;
+  info->tree_boundary = (int8_t) P8EST_CONNECT_EDGE;
+  start_idx2 = args->start_idx2 =
+    P4EST_ALLOC (int, loop_args->alloc_size / 2);
+  sc_array_init (&(info->sides), sizeof (p8est_iter_edge_side_t));
+  sc_array_init (&(args->common_corners[0]), sizeof (int));
+  sc_array_init (&(args->common_corners[1]), sizeof (int));
+  args->loop_args = loop_args;
+
+  if (edge >= 0) {
+    for (ti = ett_offset[edge]; ti < ett_offset[edge + 1]; ti++) {
+      nt = ett[ti];
+      ne = (int) ete[ti];
+      o = ne / 12;
+      ne %= 12;
+      eside = (p8est_iter_edge_side_t *) sc_array_push (&(info->sides));
+      eside->orientation = (int8_t) o;
+      eside->edge = (int8_t) ne;
+      eside->treeid = nt;
+      start_idx2[count++] = 0;
+      eside->faces[0] = -1;
+      eside->faces[1] = -1;
+    }
+  }
+  else {
+    eside = (p8est_iter_edge_side_t *) sc_array_push (&(info->sides));
+    eside->edge = (int8_t) e;
+    eside->treeid = t;
+    eside->orientation = 0;
+    start_idx2[count++] = 0;
+    eside->faces[0] = -1;
+    eside->faces[1] = -1;
+    for (i = 0; i < 2; i++) {
+      f = p8est_edge_faces[e][i];
+      nt = ttt[t * P4EST_FACES + f];
+      nf = (int) ttf[t * P4EST_FACES + f];
+      o = nf / P4EST_FACES;
+      nf %= P4EST_FACES;
+      if (nt == t && nf == f) {
+        continue;
+      }
+      c0 = p8est_edge_corners[e][0];
+      c1 = p8est_edge_corners[e][1];
+      c0 = p8est_corner_face_corners[c0][f];
+      c1 = p8est_corner_face_corners[c1][f];
+      ref = p8est_face_permutation_refs[f][nf];
+      set = p8est_face_permutation_sets[ref][o];
+      nc0 = p8est_face_permutations[set][c0];
+      nc1 = p8est_face_permutations[set][c1];
+      nc0 = p8est_face_corners[nf][nc0];
+      nc1 = p8est_face_corners[nf][nc1];
+      ne = p8est_child_corner_edges[nc0][nc1];
+      for (j = 0; j < count; j++) {
+        eside = p8est_iter_eside_array_index_int (&(info->sides), j);
+        if (eside->treeid == nt && (int) eside->edge == ne) {
+          break;
+        }
+      }
+      if (j < count) {
+        continue;
+      }
+      eside = (p8est_iter_edge_side_t *) sc_array_push (&(info->sides));
+      eside->orientation = (int8_t) ((nc0 < nc1) ? 0 : 1);
+      eside->edge = (int8_t) ne;
+      eside->treeid = nt;
+      start_idx2[count++] = 0;
+      eside->faces[0] = -1;
+      eside->faces[1] = -1;
+    }
+  }
+
+  sc_array_sort (&(info->sides), eside_compare);
+  for (i = 0; i < count; i++) {
+    eside = p8est_iter_eside_array_index_int (&(info->sides), i);
+    if (!i) {
+      o = eside->orientation;
+      eside->orientation = 0;
+    }
+    else {
+      eside->orientation ^= o;
+    }
+    cc = (int *) sc_array_push (&(common_corners[0]));
+    *cc = p8est_edge_corners[eside->edge][eside->orientation];
+    cc = (int *) sc_array_push (&(common_corners[1]));
+    *cc = p8est_edge_corners[eside->edge][1 - eside->orientation];
+  }
+
+  args->num_sides = count;
+
+  {
+    int                 faces_count = 0;
+    for (i = 0; i < count; i++) {
+      eside = p8est_iter_eside_array_index_int (&(info->sides), i);
+      nt = eside->treeid;
+      ne = eside->edge;
+      for (j = 0; j < 2; j++) {
+        if (eside->faces[j] == -1) {
+          int                 nne;
+          p4est_topidx_t      nnt;
+
+          eside->faces[j] = faces_count;
+
+          f = p8est_edge_faces[ne][j];
+          nnt = ttt[P4EST_FACES * nt + f];
+          nf = (int) ttf[nt * P4EST_FACES + f];
+          o = nf / P4EST_FACES;
+          nf %= P4EST_FACES;
+          if (nnt == nt && nf == f) {
+            faces_count++;
+            continue;
+          }
+          c0 = p8est_edge_corners[ne][0];
+          c1 = p8est_edge_corners[ne][1];
+          c0 = p8est_corner_face_corners[c0][f];
+          c1 = p8est_corner_face_corners[c1][f];
+          ref = p8est_face_permutation_refs[f][nf];
+          set = p8est_face_permutation_sets[ref][o];
+          nc0 = p8est_face_permutations[set][c0];
+          nc1 = p8est_face_permutations[set][c1];
+          nc0 = p8est_face_corners[nf][nc0];
+          nc1 = p8est_face_corners[nf][nc1];
+          nne = p8est_child_corner_edges[nc0][nc1];
+
+          for (k = 0; k < count; k++) {
+            p8est_iter_edge_side_t *eside2;
+
+            if (k == i) {
+              continue;
+            }
+            eside2 = p8est_iter_eside_array_index_int (&(info->sides), k);
+
+            if (eside2->treeid == nnt && (int) eside2->edge == nne) {
+              if (p8est_edge_faces[nne][0] == nf) {
+                eside2->faces[0] = faces_count;
+              }
+              else {
+                P4EST_ASSERT (p8est_edge_faces[nne][1] == nf);
+                eside2->faces[1] = faces_count;
+              }
+            }
+          }
+          faces_count++;
+        }
+      }
+    }
+  }
+
+  if (loop_args->loop_corner) {
+    p8est_iter_init_corner_from_edge (&(args->corner_args), args);
+  }
+  p8est_iter_init_loop_edge (loop_args, p8est, ghost_layer, info);
+}
+
+static void
+p8est_iter_reset_edge (p8est_iter_edge_args_t * args)
+{
+  if (args->loop_args->loop_corner) {
+    p4est_iter_reset_corner (&args->corner_args);
+  }
+  sc_array_reset (&(args->common_corners[0]));
+  sc_array_reset (&(args->common_corners[1]));
+  sc_array_reset (&(args->info.sides));
+  P4EST_FREE (args->start_idx2);
+}
+
+static void
+p8est_edge_iterate (p8est_iter_edge_args_t * args, void *user_data,
+                    p8est_iter_edge_t iter_edge,
+                    p8est_iter_corner_t iter_corner)
+{
+  const int           local = 0;
+  const int           ghost = 1;
+
+  p4est_iter_loop_args_t *loop_args = args->loop_args;
+  int                 num_sides = args->num_sides;
+  int                 start_level = loop_args->level;
+  int                *start_idx2 = args->start_idx2;
+  int                *level_num = loop_args->level_num;
+  sc_array_t        **quadrants = loop_args->quadrants;
+  size_t            **zindex = loop_args->index;
+  size_t             *first_index = loop_args->first_index;
+  sc_array_t         *common_corners = args->common_corners;
+  p8est_quadrant_t  **test = loop_args->test;
+  size_t             *count = loop_args->count;
+  int                *test_level = loop_args->test_level;
+  int                *quad_idx2 = loop_args->quad_idx2;
+  int8_t             *refine = loop_args->refine;
+  int                *temp_int, *temp_int2;
+  int                 i;
+  int                *Level = &(loop_args->level);
+  int                 side;
+  int                 type;
+  int                 st;
+  int                 level_idx2;
+  p8est_iter_edge_info_t *info = &(args->info);
+  p8est_iter_edge_side_t *eside;
+  p8est_quadrant_t  **quads;
+  p4est_locidx_t     *quadids;
+  int8_t             *is_ghost;
+  int                 child_corner;
+  int8_t              has_local;
+  sc_array_t          test_view;
+  int8_t              all_empty, stop_refine;
+  p4est_iter_corner_args_t *corner_args = &(args->corner_args);
+  sc_array_t         *tier_rings = loop_args->tier_rings;
+
+  /* level_idx2 moves us to the correct set of bounds within the index arrays
+   * for the level: it is a set of bounds because it includes all children at
+   * this level */
+  level_idx2 = start_level * P4EST_ITER_STRIDE;
+  for (side = 0; side < num_sides; side++) {
+
+    /* start_idx2 gives the ancestor id at level for the search area on this
+     * side, so quad_idx2[side] now gives the correct location in
+     * index[sidetype] of the bounds of the search area */
+    quad_idx2[side] = level_idx2 + start_idx2[side];
+
+    /* get the location in quadrants[sidetype] of the first quadrant in the
+     * search area, and the count of quadrants in the search area */
+    for (type = local; type <= ghost; type++) {
+      st = side * 2 + type;
+      first_index[st] = zindex[st][quad_idx2[side]];
+      count[st] = (zindex[st][quad_idx2[side] + 1] - first_index[st]);
+    }
+  }
+
+  /* edge_iterate only runs if there is a chance of a local quadrant touching
+   * the desired edge */
+  for (side = 0; side < num_sides; side++) {
+    if (count[side * 2 + local]) {
+      break;
+    }
+  }
+  if (side == num_sides) {
+    return;
+  }
+
+  /* we think of the search tree as being rooted at start_level, so we can
+   * think the branch number at start_level as 0, even if it actually is not */
+  level_num[start_level] = 0;
+
+  for (;;) {
+    /* for each sidetype, get the first quadrant in that sidetype search area
+     */
+    for (side = 0; side < num_sides; side++) {
+      for (type = local; type <= ghost; type++) {
+        st = side * 2 + type;
+        if (count[st]) {
+          test[st] = p4est_quadrant_array_index (quadrants[st],
+                                                 first_index[st]);
+          test_level[st] = (int) test[st]->level;
+        }
+        else {
+          test[st] = NULL;
+          test_level[st] = -1;
+        }
+      }
+      /* initially assume that every side needs to be refined */
+      refine[side] = 1;
+    }
+    /* initially assume that we are going to have to refine our search areas */
+    stop_refine = 0;
+    has_local = 0;
+    for (side = 0; side < num_sides; side++) {
+      for (type = local; type <= ghost; type++) {
+        st = side * 2 + type;
+        /* if the candidate from sidetype is the same size as the search area,
+         * then we do not refine this side */
+        if (test_level[st] == *Level) {
+          if (!stop_refine) {
+            stop_refine = 1;
+            /* we are not going to recur on the next level, instead moving to
+             * the next branch on this level */
+            level_num[*Level]++;
+            /* if there is no edge callback (i.e., we are just running
+             * edge_iterate to find corners), then we're done with this branch */
+            if (iter_edge == NULL) {
+              goto change_search_area;
+            }
+          }
+          /* this side is full-sized, it does not need to be refined */
+          refine[side] = 0;
+          eside = p8est_iter_eside_array_index_int (&info->sides, side);
+          eside->is_hanging = 0;
+          eside->is.full.quad = test[st];
+          eside->is.full.is_ghost = (type == ghost);
+          eside->is.full.quadid = first_index[st];
+          has_local = (has_local || (type == local));
+        }
+      }
+    }
+    for (side = 0; side < num_sides; side++) {
+      if (refine[side]) {
+        if (stop_refine && count[side * 2 + local] == 0 &&
+            count[side * 2 + ghost] == 0) {
+          /* if a side is empty, the appropriate quadrant(s) are missing from the
+           * ghost layer, so we fill in with NULL */
+          eside = p8est_iter_eside_array_index_int (&info->sides, side);
+          eside->is_hanging = 0;
+          eside->is.full.quad = NULL;
+          eside->is.full.is_ghost = 1;
+          eside->is.full.quadid = -1;
+          refine[side] = 0;
+        }
+      }
+      if (refine[side]) {
+        /* at this point, the side needs to be refined, so we take the search area
+         * and split it up, taking the indices for the refined search areas and
+         * placing them on the next tier in index[sidetype] */
+        quad_idx2[side] = level_idx2 + P4EST_ITER_STRIDE;
+        for (type = local; type <= ghost; type++) {
+          st = side * 2 + type;
+          sc_array_init_view (&test_view, quadrants[st],
+                              first_index[st], count[st]);
+          p4est_iter_tier_insert (&test_view, *Level, zindex[st] +
+                                  quad_idx2[side], first_index[st],
+                                  tier_rings, test[st]);
+        }
+        if (stop_refine) {
+          /* fill the side info */
+          eside = p8est_iter_eside_array_index_int (&(info->sides), side);
+          eside->is_hanging = 1;
+          quads = eside->is.hanging.quad;
+          is_ghost = eside->is.hanging.is_ghost;
+          quadids = eside->is.hanging.quadid;
+          for (i = 0; i < 2; i++) {
+            /* esides expects things in z-order, not the search order */
+            temp_int =
+              (int *) sc_array_index_int (&(common_corners[i]), side);
+            temp_int2 =
+              (int *) sc_array_index_int (&(common_corners[1 - i]), side);
+            if (*temp_int < *temp_int2) {
+              P4EST_ASSERT (p8est_edge_corners[eside->edge][0] == *temp_int);
+              child_corner = 0;
+            }
+            else {
+              P4EST_ASSERT (p8est_edge_corners[eside->edge][1] == *temp_int);
+              child_corner = 1;
+            }
+            quads[child_corner] = NULL;
+            is_ghost[child_corner] = 1;
+            quadids[child_corner] = -1;
+
+            /* get the location in index[sidetype] of the index for the hanging
+             * quadrant */
+            quad_idx2[side] = level_idx2 + P4EST_ITER_STRIDE + *temp_int;
+            for (type = local; type <= ghost; type++) {
+              st = side * 2 + type;
+              first_index[st] = zindex[st][quad_idx2[side]];
+              count[st] =
+                (size_t) zindex[st][quad_idx2[side] + 1] - first_index[st];
+              /* if the search area is non-empty, by the two to one condition
+               * it must contain exactly quadrant half as large as the search
+               * level, which we add to the collection */
+              if (count[st]) {
+                quads[child_corner] = p4est_quadrant_array_index
+                  (quadrants[st], first_index[st]);
+                P4EST_ASSERT ((int) quads[child_corner]->level == *Level + 1);
+                is_ghost[child_corner] = (type == ghost);
+                quadids[child_corner] = (p4est_locidx_t) first_index[st];
+                has_local = (has_local || (type == local));
+              }
+            }
+          }
+        }                       /* if (stop_refine) */
+      }                         /* if (refine[side]) */
+    }                           /* for (side = 0; side < num_sides; side++) */
+    if (stop_refine) {
+      if (has_local) {
+        /* if there is a local quadrant, we run the callback */
+        iter_edge (info, user_data);
+      }
+      else if (args->remote) {
+        /* if we are supposed to run on remotely touched edges, determine if
+         * the edge is remotely touched first */
+        p4est_t            *p4est = info->p4est;
+        p4est_quadrant_t   *fq =
+          &(p4est->global_first_position[p4est->mpirank]);
+        p4est_quadrant_t   *lq =
+          &(p4est->global_first_position[p4est->mpirank + 1]);
+        p4est_topidx_t      flt = fq->p.which_tree;
+        p4est_topidx_t      llt = lq->p.which_tree;
+
+        for (side = 0; side < num_sides; side++) {
+          int                 dir;
+          p4est_topidx_t      ht;
+
+          eside = p8est_iter_eside_array_index_int (&info->sides, side);
+          ht = eside->treeid;
+          if (!eside->is_hanging || eside->is.hanging.quad[0] == NULL ||
+              eside->is.hanging.quad[1] == NULL) {
+            continue;
+          }
+          for (dir = 0; dir < 2; dir++) {
+            int                 f = eside->faces[dir];
+            int                 side2;
+
+            for (side2 = 0; side2 < num_sides; side2++) {
+              p8est_iter_edge_side_t *eside2;
+              int                 dir2;
+
+              if (side2 == side) {
+                continue;
+              }
+              eside2 = p8est_iter_eside_array_index_int (&info->sides, side2);
+              if (eside2->is_hanging || eside2->is.full.quad == NULL) {
+                continue;
+              }
+              for (dir2 = 0; dir2 < 2; dir2++) {
+                if (eside2->faces[dir2] == f) {
+                  p4est_quadrant_t    h;
+                  int                 c1, c2, fc[2];
+                  int                 face;
+
+                  face = p8est_edge_faces[eside->edge][dir];
+                  c1 = p8est_edge_corners[eside->edge][0];
+                  c2 = p8est_edge_corners[eside->edge][1];
+                  fc[0] = p8est_corner_face_corners[c1][face];
+                  fc[1] = p8est_corner_face_corners[c2][face];
+                  fc[0] = p8est_face_corners[face][fc[0] ^ 3];
+                  fc[1] = p8est_face_corners[face][fc[1] ^ 3];
+                  for (i = 0; i < 2; i++) {
+                    p4est_quadrant_sibling (eside->is.hanging.quad[0], &h,
+                                            fc[i]);
+                    if ((ht > flt
+                         || (ht == flt
+                             && p4est_quadrant_disjoint (fq, &h) <= 0))
+                        && (ht < llt
+                            || (ht == llt
+                                && p4est_quadrant_disjoint (&h, lq) < 0))) {
+                      iter_edge (info, user_data);
+                      /* this goto is to avoid a 5-level break */
+                      goto change_search_area;
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }                           /* if (stop_refine) */
+    else {
+      /* if every side needed to be refined, then we descend along this branch to
+       * the next level and search there */
+      level_num[++(*Level)] = 0;
+      level_idx2 += P4EST_ITER_STRIDE;
+    }
+
+  change_search_area:
+
+    for (;;) {
+      /* if we tried to advance the search area on start_level, we've completed
+       * the search */
+      if (level_num[start_level] > 0) {
+        P4EST_ASSERT (*Level == start_level);
+        return;
+      }
+      /* if we have tried to advance the search area past two branches, that
+       * means that we have completed all of the branches on this level */
+      if (level_num[*Level] == 2) {
+        /* if we have a corner callback, we need to run it on the corner between
+         * the edge branches on this level */
+        if (loop_args->loop_corner) {
+          P4EST_ASSERT (corner_args->num_sides == 2 * num_sides);
+          p4est_iter_copy_indices (loop_args, corner_args->start_idx2,
+                                   num_sides, 2);
+          p4est_corner_iterate (corner_args, user_data, iter_corner);
+        }
+        /* now that we're done on this level, go up a level and over a branch */
+        level_num[--(*Level)]++;
+        level_idx2 -= P4EST_ITER_STRIDE;
+      }
+      else {
+        /* at this point, we need to initialize the bounds of the search areas
+         * for this new branch */
+        all_empty = 1;
+        for (side = 0; side < num_sides; side++) {
+          temp_int = (int *) sc_array_index_int
+            (&(common_corners[level_num[*Level]]), side);
+          quad_idx2[side] = level_idx2 + *temp_int;
+          for (type = local; type <= ghost; type++) {
+            st = side * 2 + type;
+            first_index[st] = zindex[st][quad_idx2[side]];
+            count[st] = (zindex[st][quad_idx2[side] + 1] - first_index[st]);
+            if (type == local && count[st]) {
+              all_empty = 0;
+            }
+          }
+        }
+        if (all_empty) {
+          /* if there are no local quadrants in any of the search areas, we're done
+           * with this search area and proceed to the next branch on this level */
+          level_num[*Level]++;
+        }
+        else {
+          /* otherwise we are done changing the search area */
+          break;
+        }
+      }
+    }                           /* change_search_area for (;;) */
+  }                             /* outer for (;;) */
+}
+#endif
+
+/* face iterate functions */
+typedef struct p4est_iter_face_args
+{
+  p4est_iter_loop_args_t *loop_args;
+  int                 start_idx2[2];
+  /* when a search branch is refined,
+     num_to_child says which child id
+     corresponds to the branch number for
+     each side of the face. e.g. Suppose
+     face[left] = 1, face[right] = 0, and
+     orientation = 0 in 3D. The child ids
+     of the descendants of the current
+     search area that touch face[left]
+     are 1, 3, 5, 7, and given
+     face[right] and the orientation, the
+     descendants that are opposite them
+     are 0, 2, 4, 6, respectively:
+     therefore num_to_child =
+     { 1, 3, 5, 7, 0, 2, 4, 6} */
+  int                 num_to_child[P4EST_CHILDREN];
+  int8_t              outside_face;     /* indicates if we are at a tree
+                                           boundary without a neighbor across
+                                           the face */
+#ifdef P4_TO_P8
+  p8est_iter_edge_args_t edge_args[2][2];
+#endif
+  p4est_iter_corner_args_t corner_args;
+  p4est_iter_face_info_t info;
+  int                 remote;
+}
+p4est_iter_face_args_t;
+
+/* given valid face arguments, setup corner arguments for a corner search that
+ * is called for a corner where four adjacent, coplaner faces meet.
+ */
+static void
+p4est_iter_init_corner_from_face (p4est_iter_corner_args_t * args,
+                                  p4est_iter_face_args_t * face_args)
+{
+  const int           ntc_str = P4EST_CHILDREN / 2;
+  int                 j, k;
+  p4est_iter_corner_info_t *info = &(args->info);
+  p4est_iter_face_side_t *fside;
+  p4est_iter_corner_side_t *cside;
+  int                *num_to_child = face_args->num_to_child;
+  int                *c_start_idx2;
+  int                 limit = face_args->outside_face ? 1 : 2;
+  int                 count = 0;
+  int                 dir;
+#ifdef P4_TO_P8
+  int                 ndir1, ndir2;
+#endif
+
+  info->p4est = face_args->info.p4est;
+  info->ghost_layer = face_args->info.ghost_layer;
+  info->tree_boundary = face_args->info.tree_boundary;
+  sc_array_init (&(info->sides), sizeof (p4est_iter_corner_side_t));
+  args->num_sides = limit * ntc_str;
+  sc_array_resize (&(info->sides), (size_t) args->num_sides);
+  c_start_idx2 = args->start_idx2 = P4EST_ALLOC (int, args->num_sides);
+  args->loop_args = face_args->loop_args;
+
+  for (j = 0; j < ntc_str; j++) {
+    for (k = 0; k < limit; k++) {
+      fside = p4est_iter_fside_array_index_int (&face_args->info.sides, k);
+      cside = p4est_iter_cside_array_index_int (&info->sides, count);
+      cside->treeid = fside->treeid;
+
+      dir = fside->face / 2;
+      cside->corner = (int8_t) num_to_child[k * ntc_str + (ntc_str - 1 - j)];
+      c_start_idx2[count++] = num_to_child[k * ntc_str + j];
+      cside->faces[dir] = j;
+#ifndef P4_TO_P8
+      cside->faces[dir ^ 1] = ntc_str + k;
+#else
+      cside->edges[dir] = ntc_str + k;
+      ndir1 = SC_MIN (((dir + 1) % 3), ((dir + 2) % 3));
+      ndir2 = SC_MAX (((dir + 1) % 3), ((dir + 2) % 3));
+      if (!k) {
+        cside->faces[ndir1] = ntc_str + ntc_str * k + (j >> 1);
+        cside->faces[ndir2] = ntc_str + ntc_str * k + 2 + (j & 1);
+        cside->edges[ndir1] = (j & 1);
+        cside->edges[ndir2] = 2 + (j >> 1);
+      }
+      else {
+        int                 c0 =
+          p8est_corner_face_corners[num_to_child[ntc_str]][fside->face];
+        int                 c1 =
+          p8est_corner_face_corners[num_to_child[ntc_str + 1]][fside->face];
+
+        if (c0 == (c1 ^ 1)) {
+          cside->faces[ndir1] = ntc_str + ntc_str * k + (j >> 1);
+          cside->faces[ndir2] = ntc_str + ntc_str * k + 2 + (j & 1);
+          cside->edges[ndir1] = (j & 1);
+          cside->edges[ndir2] = 2 + (j >> 1);
+        }
+        else {
+          P4EST_ASSERT (c0 == (c1 ^ 2));
+          cside->faces[ndir2] = ntc_str + ntc_str * k + (j >> 1);
+          cside->faces[ndir1] = ntc_str + ntc_str * k + 2 + (j & 1);
+          cside->edges[ndir2] = (j & 1);
+          cside->edges[ndir1] = 2 + (j >> 1);
+        }
+      }
+#endif
+    }
+  }
+  args->remote = face_args->remote;
+}
+
+/* given valid face arguments, setup edge arguments for an edge search that is
+ * called for an edge where two adjacent, coplaner faces meet: each plane
+ * has two directions, and each direction can begin from one of two potential
+ * starting sides.
+ */
+#ifdef P4_TO_P8
+static void
+p8est_iter_init_edge_from_face (p8est_iter_edge_args_t * args,
+                                p4est_iter_face_args_t * face_args,
+                                int dir, int side)
+{
+  const int           ntc_str = P4EST_CHILDREN / 2;
+  int                 j, k;
+  int                 c0, c1, *cc;
+  p8est_iter_edge_info_t *info = &(args->info);
+  p8est_iter_face_side_t *fside;
+  p8est_iter_edge_side_t *eside;
+  int                *num_to_child = face_args->num_to_child;
+  int                *e_start_idx2;
+  int                 limit = face_args->outside_face ? 1 : 2;
+  int                 count = 0;
+  int                 pos[2][2];
+  sc_array_t         *common_corners = args->common_corners;
+
+  pos[0][0] = 0;
+  pos[0][1] = dir ? 2 : 1;
+  pos[1][0] = dir ? 1 : 2;
+  pos[1][1] = 3;
+
+  info->p4est = face_args->info.p4est;
+  info->ghost_layer = face_args->info.ghost_layer;
+  info->tree_boundary = face_args->info.tree_boundary;
+  sc_array_init (&(info->sides), sizeof (p8est_iter_edge_side_t));
+  args->num_sides = limit * ntc_str / 2;
+  sc_array_resize (&(info->sides), (size_t) args->num_sides);
+  sc_array_init (&(common_corners[0]), sizeof (int));
+  sc_array_init (&(common_corners[1]), sizeof (int));
+  sc_array_resize (&(common_corners[0]), (size_t) args->num_sides);
+  sc_array_resize (&(common_corners[1]), (size_t) args->num_sides);
+  e_start_idx2 = args->start_idx2 = P4EST_ALLOC (int, args->num_sides);
+  args->loop_args = face_args->loop_args;
+
+  for (j = 0; j < 2; j++) {
+    for (k = 0; k < limit; k++) {
+      cc = (int *) sc_array_index_int (&(common_corners[0]), count);
+      c0 = *cc = num_to_child[k * ntc_str + pos[1 - j][0]];
+      cc = (int *) sc_array_index_int (&(common_corners[1]), count);
+      c1 = *cc = num_to_child[k * ntc_str + pos[1 - j][1]];
+      fside = p4est_iter_fside_array_index_int (&face_args->info.sides, k);
+      eside = p8est_iter_eside_array_index_int (&(info->sides), count);
+      eside->orientation = (int8_t) ((c0 < c1) ? 0 : 1);
+      eside->treeid = fside->treeid;
+      eside->edge = (int8_t) p8est_child_corner_edges[c0][c1];
+      e_start_idx2[count++] = num_to_child[k * ntc_str + pos[j][side]];
+      if (p8est_edge_faces[eside->edge][0] == fside->face) {
+        eside->faces[0] = j;
+        eside->faces[1] = 2 + k;
+      }
+      else {
+        eside->faces[0] = 2 + k;
+        eside->faces[1] = j;
+      }
+    }
+  }
+
+  /* also initialize the corner that is in the middle of the edge */
+  args->remote = face_args->remote;
+  if (args->loop_args->loop_corner) {
+    p8est_iter_init_corner_from_edge (&(args->corner_args), args);
+  }
+}
+#endif
+
+#if 0                           /* currently unused */
+
+static int
+fside_compare (const void *a, const void *b)
+{
+  p4est_locidx_t      diff;
+  const p4est_iter_face_side_t *A = (const p4est_iter_face_side_t *) a;
+  const p4est_iter_face_side_t *B = (const p4est_iter_face_side_t *) b;
+
+  diff = (A->treeid - B->treeid);
+  if (diff) {
+    return (int) diff;
+  }
+  return (A->face - B->face);
+}
+
+#endif /* 0 */
+
+/* initialize face args for a face between trees */
+static void
+p4est_iter_init_face (p4est_iter_face_args_t * args, p4est_t * p4est,
+                      p4est_ghost_t * ghost_layer,
+                      p4est_iter_loop_args_t * loop_args, p4est_topidx_t t,
+                      int f)
+{
+  const int           ntc_str = P4EST_CHILDREN / 2;
+  int                 i;
+  int                 c;
+  int                 count = 0;
+  p4est_iter_face_info_t *info = &(args->info);
+  p4est_iter_face_side_t *fside;
+  int                *num_to_child = args->num_to_child;
+  int                *start_idx2 = args->start_idx2;
+#ifdef P4_TO_P8
+  int                 ref, set;
+#endif
+  p4est_connectivity_t *conn = p4est->connectivity;
+  p4est_topidx_t      nt = conn->tree_to_tree[t * P4EST_FACES + f];
+  int                 nf = (int) conn->tree_to_face[t * P4EST_FACES + f];
+  int                 o = nf / P4EST_FACES;
+
+  nf %= P4EST_FACES;
+
+  if (nt < t || (nt == t && nf < f)) {
+    p4est_iter_init_face (args, p4est, ghost_layer, loop_args, nt, nf);
+    return;
+  }
+
+  args->loop_args = loop_args;
+  info->p4est = p4est;
+  info->ghost_layer = ghost_layer;
+  info->tree_boundary = (int8_t) P4EST_CONNECT_FACE;
+  sc_array_init (&(info->sides), sizeof (p4est_iter_face_side_t));
+
+#ifdef P4_TO_P8
+  ref = p8est_face_permutation_refs[f][nf];
+  set = p8est_face_permutation_sets[ref][o];
+#endif
+
+  if (t == nt && nf == f) {
+    nt = -1;
+  }
+
+  args->outside_face = (nt == -1);
+
+  fside = (p4est_iter_face_side_t *) sc_array_push (&(info->sides));
+  fside->face = (int8_t) f;
+  fside->treeid = t;
+  start_idx2[count++] = 0;
+  o = info->orientation = 0;
+
+  if (nt != -1) {
+    fside = (p4est_iter_face_side_t *) sc_array_push (&(info->sides));
+    fside->treeid = nt;
+    fside->face = (int8_t) nf;
+    start_idx2[count++] = 0;
+    o = info->orientation =
+      conn->tree_to_face[t * P4EST_FACES + f] / P4EST_FACES;
+  }
+
+  /* for each corner, find the touching corner on the other tree */
+  for (i = 0; i < ntc_str; i++) {
+    c = p4est_face_corners[f][i];
+    num_to_child[i] = c;
+    if (nt != -1) {
+#ifndef P4_TO_P8
+      num_to_child[ntc_str + i] = p4est_face_corners[nf][o == 0 ? i : 1 - i];
+#else
+      num_to_child[ntc_str + i] = p8est_face_corners[nf]
+        [p8est_face_permutations[set][i]];
+#endif
+    }
+  }
+
+#ifdef P4_TO_P8
+  if (loop_args->loop_edge) {
+    p8est_iter_init_edge_from_face (&(args->edge_args[0][0]), args, 0, 0);
+    p8est_iter_init_edge_from_face (&(args->edge_args[0][1]), args, 0, 1);
+    p8est_iter_init_edge_from_face (&(args->edge_args[1][0]), args, 1, 0);
+    p8est_iter_init_edge_from_face (&(args->edge_args[1][1]), args, 1, 1);
+  }
+#endif
+  if (loop_args->loop_corner) {
+    p4est_iter_init_corner_from_face (&(args->corner_args), args);
+  }
+
+  if (nt != -1) {
+    p4est_iter_init_loop_face (loop_args, t, nt, p4est, ghost_layer);
+  }
+  else {
+    p4est_iter_init_loop_outside_face (loop_args, t, p4est, ghost_layer);
+  }
+}
+
+static void
+p4est_iter_reset_face (p4est_iter_face_args_t * args)
+{
+  if (args->loop_args->loop_corner) {
+    p4est_iter_reset_corner (&(args->corner_args));
+  }
+#ifdef P4_TO_P8
+  if (args->loop_args->loop_edge) {
+    p8est_iter_reset_edge (&(args->edge_args[0][0]));
+    p8est_iter_reset_edge (&(args->edge_args[0][1]));
+    p8est_iter_reset_edge (&(args->edge_args[1][0]));
+    p8est_iter_reset_edge (&(args->edge_args[1][1]));
+  }
+#endif
+  sc_array_reset (&(args->info.sides));
+}
+
+static void
+p4est_face_iterate (p4est_iter_face_args_t * args, void *user_data,
+                    p4est_iter_face_t iter_face,
+#ifdef P4_TO_P8
+                    p8est_iter_edge_t iter_edge,
+#endif
+                    p4est_iter_corner_t iter_corner)
+{
+
+  const int           left = 0;
+  const int           right = 1;
+  const int           local = 0;
+  const int           ghost = 1;
+  const int           ntc_str = P4EST_CHILDREN / 2;
+
+  p4est_iter_loop_args_t *loop_args = args->loop_args;
+  int                 start_level = loop_args->level;
+  int                *start_idx2 = args->start_idx2;
+  int                *level_num = loop_args->level_num;
+  sc_array_t        **quadrants = loop_args->quadrants;
+  size_t            **zindex = loop_args->index;
+  size_t             *first_index = loop_args->first_index;
+  int                *num_to_child = args->num_to_child;
+  p4est_quadrant_t  **test = loop_args->test;
+  size_t             *count = loop_args->count;
+  int                *test_level = loop_args->test_level;
+  int                *quad_idx2 = loop_args->quad_idx2;
+  int8_t             *refine = loop_args->refine;
+  int                 limit;
+
+  int                 i;
+  int                *Level = &(loop_args->level);
+  int                 side;
+  int                 type;
+  int                 st;
+  int                 level_idx2;
+  p4est_iter_face_info_t *info = &(args->info);
+  p4est_iter_face_side_t *fside;
+  p4est_quadrant_t  **quads;
+  p4est_locidx_t     *quadids;
+  int8_t             *is_ghost;
+  int                 child_corner;
+  int8_t              has_local;
+  sc_array_t          test_view;
+  p4est_iter_corner_args_t *corner_args = &(args->corner_args);
+  sc_array_t         *tier_rings = loop_args->tier_rings;
+#ifdef P4_TO_P8
+  int                 dir;
+#endif
+  int                 stop_refine;
+  int                 all_empty;
+
+  /* if we are at an outside face, then there is no right half to our search
+   * that needs to be coordinated with the left half */
+  limit = args->outside_face ? left : right;
+
+  /* level_idx2 moves us to the correct set of bounds within the index arrays
+   * for the level: it is a set of bounds because it includes all children at
+   * this level */
+  level_idx2 = start_level * P4EST_ITER_STRIDE;
+
+  for (side = left; side <= limit; side++) {
+
+    /* start_idx2 gives the ancestor id at level for the search area on this
+     * side, so quad_idx2[side] now gives the correct location in
+     * zindex[sidetype] of the bounds of the search area */
+    quad_idx2[side] = level_idx2 + start_idx2[side];
+
+    /* get the location in quadrants[sidetype] of the first quadrant in the
+     * search area, and the count of quadrants in the search area */
+    for (type = local; type <= ghost; type++) {
+      st = side * 2 + type;
+      first_index[st] = zindex[st][quad_idx2[side]];
+      count[st] = (zindex[st][quad_idx2[side] + 1] - first_index[st]);
+    }
+  }
+
+  /* face_iterate only runs if there is a chance of a local quadrant touching
+   * the desired face */
+  if (!args->outside_face) {
+    if (!count[left * 2 + local] && !count[right * 2 + local]) {
+      return;
+    }
+  }
+  else {
+    if (!count[left * 2 + local]) {
+      return;
+    }
+  }
+
+  /* we think of the search tree as being rooted at start_level, so we can
+   * think the branch number at start_level as 0, even if it actually is not */
+  level_num[start_level] = 0;
+  for (;;) {
+    /* for each sidetype, get the first quadrant in that sidetype search area
+     */
+    for (side = left; side <= limit; side++) {
+      for (type = local; type <= ghost; type++) {
+        st = side * 2 + type;
+        if (count[st]) {
+          test[st] = p4est_quadrant_array_index (quadrants[st],
+                                                 first_index[st]);
+          test_level[st] = (int) test[st]->level;
+        }
+        else {
+          test[st] = NULL;
+          test_level[st] = -1;
+        }
+      }
+    }
+    /* initially assume that each side needs to be refined */
+    refine[left] = refine[right] = 1;
+    stop_refine = 0;
+    has_local = 0;
+
+    /* get a candidate from each sidetype */
+    for (side = left; side <= limit; side++) {
+      for (type = local; type <= ghost; type++) {
+        st = side * 2 + type;
+        /* if the candidate from sidetype is the same size as the search area,
+         * then we do not refine this side */
+        if (test_level[st] == *Level) {
+          if (!stop_refine) {
+            stop_refine = 1;
+            /* we are not going to recur on the next level, instead moving to
+             * the next branch on this level */
+            level_num[*Level]++;
+            /* if there is no face callback (i.e., we are just running
+             * face_iterate to find edges and corners), then we're done with
+             * this branch */
+            if (iter_face == NULL) {
+              goto change_search_area;
+            }
+          }
+          P4EST_ASSERT (count[st] == 1);
+          P4EST_ASSERT (count[side * 2 + (type ^ 1)] == 0);
+          refine[side] = 0;
+          fside = p4est_iter_fside_array_index_int (&(info->sides), side);
+          fside->is_hanging = 0;
+          fside->is.full.quad = test[st];
+          fside->is.full.quadid = (p4est_locidx_t) first_index[st];
+          has_local = (has_local || (type == local));
+          fside->is.full.is_ghost = (type == ghost);
+        }
+      }
+    }
+    for (side = left; side <= limit; side++) {
+      if (refine[side]) {
+        if (stop_refine && count[side * 2 + local] == 0 &&
+            count[side * 2 + ghost] == 0) {
+          fside = p4est_iter_fside_array_index_int (&info->sides, side);
+          fside->is_hanging = 0;
+          fside->is.full.quad = NULL;
+          fside->is.full.is_ghost = 1;
+          fside->is.full.quadid = -1;
+          refine[side] = 0;
+        }
+      }
+      if (refine[side]) {
+        quad_idx2[side] = level_idx2 + P4EST_ITER_STRIDE;
+        for (type = local; type <= ghost; type++) {
+          st = side * 2 + type;
+          sc_array_init_view (&test_view, quadrants[st],
+                              first_index[st], count[st]);
+          p4est_iter_tier_insert (&test_view, *Level, zindex[st] +
+                                  quad_idx2[side], first_index[st],
+                                  tier_rings, test[st]);
+        }
+        if (stop_refine) {
+          fside = p4est_iter_fside_array_index_int (&(info->sides), side);
+          fside->is_hanging = 1;
+          quads = fside->is.hanging.quad;
+          quadids = fside->is.hanging.quadid;
+          is_ghost = fside->is.hanging.is_ghost;
+          for (i = 0; i < P4EST_CHILDREN / 2; i++) {
+            /* fside expects the hanging quadrants listed in z order, not search
+             * order */
+            child_corner = num_to_child[side * ntc_str + i];
+            child_corner =
+              p4est_corner_face_corners[child_corner][fside->face];
+            quads[child_corner] = NULL;
+            quadids[child_corner] = -1;
+            is_ghost[child_corner] = 1;
+            quad_idx2[side] = level_idx2 + P4EST_ITER_STRIDE +
+              num_to_child[side * ntc_str + i];
+            for (type = local; type <= ghost; type++) {
+              st = side * 2 + type;
+              first_index[st] = zindex[st][quad_idx2[side]];
+              count[st] = zindex[st][quad_idx2[side] + 1] - first_index[st];
+              /* if the search area is non-empty, by the two to one condition
+               * it must contain exactly one quadrant: if one of the two types
+               * is local, we run iter_face */
+              if (count[st]) {
+                quads[child_corner] = p4est_quadrant_array_index
+                  (quadrants[st], first_index[st]);
+                P4EST_ASSERT ((int) quads[child_corner]->level == *Level + 1);
+                quadids[child_corner] = (p4est_locidx_t) first_index[st];
+                is_ghost[child_corner] = (type == ghost);
+                has_local = (has_local || (type == local));
+              }
+            }
+          }
+        }
+      }
+    }
+    if (stop_refine) {
+      if (has_local) {
+        iter_face (info, user_data);
+      }
+    }
+    else {
+      /* if we refined both sides, we descend to the next level from this branch
+       * and continue searching there */
+      level_num[++(*Level)] = 0;
+      level_idx2 += P4EST_ITER_STRIDE;
+    }
+
+  change_search_area:
+
+    for (;;) {
+      /* if we tried to advance the search area on start_level, we've completed
+       * the search */
+      if (level_num[start_level] > 0) {
+        P4EST_ASSERT (*Level == start_level);
+        return;
+      }
+
+      /* if we have tried to advance the search area past the number of
+       * descendants, that means that we have completed all of the branches on
+       * this level */
+      if (level_num[*Level] == P4EST_CHILDREN / 2) {
+#ifdef P4_TO_P8
+        /* if we have an edge callback, we need to run it on all of the edges
+         * between the face branches on this level */
+        if (loop_args->loop_edge) {
+          for (dir = 0; dir < 2; dir++) {
+            for (side = 0; side < 2; side++) {
+              P4EST_ASSERT (args->edge_args[dir][side].num_sides ==
+                            2 * (limit + 1));
+              p4est_iter_copy_indices (loop_args,
+                                       args->edge_args[dir][side].start_idx2,
+                                       limit + 1, 2);
+              p8est_edge_iterate (&(args->edge_args[dir][side]), user_data,
+                                  iter_edge, iter_corner);
+            }
+          }
+        }
+#endif
+        /* if we have a corner callback, we need to run it on the corner between
+         * the face branches on this level */
+        if (iter_corner != NULL) {
+          P4EST_ASSERT (corner_args->num_sides ==
+                        (P4EST_CHILDREN / 2) * (limit + 1));
+          p4est_iter_copy_indices (loop_args, corner_args->start_idx2,
+                                   limit + 1, P4EST_HALF);
+          p4est_corner_iterate (corner_args, user_data, iter_corner);
+        }
+
+        /* now that we're done on this level, go up a level and over a branch */
+        level_num[--(*Level)]++;
+        level_idx2 -= P4EST_ITER_STRIDE;
+      }
+      else {
+        /* at this point, we need to initialize the bounds of the search areas
+         * for this new branch */
+        all_empty = 1;
+        for (side = left; side <= limit; side++) {
+          quad_idx2[side] =
+            level_idx2 + num_to_child[side * ntc_str + level_num[*Level]];
+        }
+        for (side = left; side <= limit; side++) {
+          for (type = local; type <= ghost; type++) {
+            st = side * 2 + type;
+            first_index[st] = zindex[st][quad_idx2[side]];
+            count[st] = (zindex[st][quad_idx2[side] + 1] - first_index[st]);
+            if (type == local && count[st]) {
+              all_empty = 0;
+            }
+          }
+        }
+        if (all_empty) {
+          /* if there are no local quadrants in either of the search areas, we're
+           * done with this search area and proceed to the next branch on this
+           * level */
+          level_num[*Level]++;
+        }
+        else {
+          /* otherwise we are done changing the search area */
+          break;
+        }
+      }
+    }
+  }
+}
+
+/* volume iterate functions */
+typedef struct p4est_iter_volume_args
+{
+  p4est_iter_loop_args_t *loop_args;
+  int                 start_idx2;
+  p4est_iter_face_args_t face_args[P4EST_DIM][P4EST_CHILDREN / 2];
+#ifdef P4_TO_P8
+  p8est_iter_edge_args_t edge_args[P4EST_DIM][2];
+#endif
+  p4est_iter_corner_args_t corner_args;
+  p4est_iter_volume_info_t info;
+  int                 remote;
+}
+p4est_iter_volume_args_t;
+
+/* given valid volume arguments, setup face arguments for a face search that is
+ * called for a face between two adjacent volumes: there are P4EST_DIM
+ * directions the face can be oriented, and each direction can be run in a
+ * different position, based on the child_ids of the two volumes on either side
+ * of it.
+ */
+static void
+p4est_iter_init_face_from_volume (p4est_iter_face_args_t * args,
+                                  p4est_iter_volume_args_t * volume_args,
+                                  int dir, int pos)
+{
+  const int           ntc_str = P4EST_CHILDREN / 2;
+  int                 i, j;
+  p4est_iter_face_info_t *info = &(args->info);
+  p4est_iter_face_side_t *fside;
+
+  info->p4est = volume_args->info.p4est;
+  info->ghost_layer = volume_args->info.ghost_layer;
+  info->orientation = 0;
+  info->tree_boundary = 0;
+  sc_array_init (&(info->sides), sizeof (p4est_iter_face_side_t));
+  sc_array_resize (&(info->sides), 2);
+  args->loop_args = volume_args->loop_args;
+  args->outside_face = 0;
+
+  args->start_idx2[0] = p4est_face_corners[dir * 2][pos];
+  args->start_idx2[1] = p4est_face_corners[dir * 2 + 1][pos];
+
+  for (i = 0; i < 2; i++) {
+    for (j = 0; j < ntc_str; j++) {
+      args->num_to_child[i * ntc_str + j] =
+        p4est_face_corners[dir * 2 + (1 - i)][j];
+    }
+  }
+
+  fside = p4est_iter_fside_array_index_int (&info->sides, 0);
+  fside->treeid = volume_args->info.treeid;
+  fside->face = (int8_t) (2 * dir + 1);
+  fside = p4est_iter_fside_array_index_int (&info->sides, 1);
+  fside->treeid = volume_args->info.treeid;
+  fside->face = (int8_t) 2 *dir;
+
+  args->remote = volume_args->remote;
+#ifdef P4_TO_P8
+  if (args->loop_args->loop_edge) {
+    p8est_iter_init_edge_from_face (&(args->edge_args[0][0]), args, 0, 0);
+    p8est_iter_init_edge_from_face (&(args->edge_args[0][1]), args, 0, 1);
+    p8est_iter_init_edge_from_face (&(args->edge_args[1][0]), args, 1, 0);
+    p8est_iter_init_edge_from_face (&(args->edge_args[1][1]), args, 1, 1);
+  }
+#endif
+  if (args->loop_args->loop_corner) {
+    p4est_iter_init_corner_from_face (&(args->corner_args), args);
+  }
+}
+
+/* given valid volume arguments, setup edge arguments for an edge search that
+ * is called for an edge between four adjacent volumes: there are P4EST_DIM
+ * directions the edge can be oriented, and each direction can be run in oen
+ * of two positioins, based on the child_ids of the four volumes surrounding
+ * it.
+ */
+#ifdef P4_TO_P8
+static void
+p8est_iter_init_edge_from_volume (p8est_iter_edge_args_t * args,
+                                  p4est_iter_volume_args_t * volume_args,
+                                  int dir, int side)
+{
+  int                 i;
+  int                *cc;
+  p8est_iter_edge_info_t *info = &(args->info);
+  p8est_iter_edge_side_t *eside;
+  sc_array_t         *common_corners = args->common_corners;
+
+  info->p4est = volume_args->info.p4est;
+  info->ghost_layer = volume_args->info.ghost_layer;
+  info->tree_boundary = 0;
+  sc_array_init (&(info->sides), sizeof (p8est_iter_edge_side_t));
+  sc_array_resize (&(info->sides), 4);
+  sc_array_init (&(common_corners[0]), sizeof (int));
+  sc_array_init (&(common_corners[1]), sizeof (int));
+  sc_array_resize (&(common_corners[0]), 4);
+  sc_array_resize (&(common_corners[1]), 4);
+  args->start_idx2 = P4EST_ALLOC (int, 4);
+  args->loop_args = volume_args->loop_args;
+  args->num_sides = 4;
+
+  for (i = 0; i < 4; i++) {
+    args->start_idx2[i] = p8est_face_corners[dir * 2 + side][i];
+    cc = (int *) sc_array_index_int (&(common_corners[0]), i);
+    *cc = p8est_face_corners[dir * 2][3 - i];
+    cc = (int *) sc_array_index_int (&(common_corners[1]), i);
+    *cc = p8est_face_corners[dir * 2 + 1][3 - i];
+    eside = p8est_iter_eside_array_index_int (&info->sides, i);
+    eside->treeid = volume_args->info.treeid;
+    eside->orientation = 0;
+    eside->edge = (int8_t) (4 * dir + (3 - i));
+    eside->faces[0] = (i >> 1);
+    eside->faces[1] = 2 + (i & 1);
+  }
+
+  args->remote = volume_args->remote;
+  if (args->loop_args->loop_corner) {
+    p8est_iter_init_corner_from_edge (&(args->corner_args), args);
+  }
+}
+#endif
+
+/* given valid volume arguments, setup corner arguments for a corner search
+ * that is called for a corner between P4EST_CHILDREN adjacent volumes.
+ */
+static void
+p4est_iter_init_corner_from_volume (p4est_iter_corner_args_t * args,
+                                    p4est_iter_volume_args_t * volume_args)
+{
+  int                 i;
+  p4est_iter_corner_info_t *info = &(args->info);
+  p4est_iter_corner_side_t *cside;
+
+  info->p4est = volume_args->info.p4est;
+  info->ghost_layer = volume_args->info.ghost_layer;
+  info->tree_boundary = 0;
+  sc_array_init (&(info->sides), sizeof (p4est_iter_corner_side_t));
+  sc_array_resize (&(info->sides), P4EST_CHILDREN);
+  args->start_idx2 = P4EST_ALLOC (int, P4EST_CHILDREN);
+  args->num_sides = P4EST_CHILDREN;
+  args->loop_args = volume_args->loop_args;
+
+  for (i = 0; i < P4EST_CHILDREN; i++) {
+    args->start_idx2[i] = i;
+    cside = p4est_iter_cside_array_index_int (&(info->sides), i);
+    cside->treeid = volume_args->info.treeid;
+    cside->corner = (int8_t) (P4EST_CHILDREN - 1 - i);
+#ifndef P4_TO_P8
+    cside->faces[0] = (i >> 1);
+    cside->faces[1] = 2 + (i & 1);
+#else
+    cside->faces[0] = (i >> 1);
+    cside->faces[1] = 4 + ((i & 4) >> 1) + (i & 1);
+    cside->faces[2] = 8 + (i & 3);
+    cside->edges[0] = (i & 1);
+    cside->edges[1] = 2 + ((i & 2) >> 1);
+    cside->edges[2] = 4 + (i >> 2);
+#endif
+  }
+  args->remote = volume_args->remote;
+}
+
+/* initialize volume arguments for a search in a tree */
+static void
+p4est_iter_init_volume (p4est_iter_volume_args_t * args, p4est_t * p4est,
+                        p4est_ghost_t * ghost_layer,
+                        p4est_iter_loop_args_t * loop_args, p4est_topidx_t t)
+{
+  int                 i, j;
+
+  args->loop_args = loop_args;
+  args->info.p4est = p4est;
+  args->info.ghost_layer = ghost_layer;
+  args->info.treeid = t;
+  args->start_idx2 = 0;
+
+  for (i = 0; i < P4EST_DIM; i++) {
+    for (j = 0; j < P4EST_CHILDREN / 2; j++) {
+      p4est_iter_init_face_from_volume (&(args->face_args[i][j]), args, i, j);
+    }
+#ifdef P4_TO_P8
+    if (loop_args->loop_edge) {
+      for (j = 0; j < 2; j++) {
+        p8est_iter_init_edge_from_volume (&(args->edge_args[i][j]), args, i,
+                                          j);
+      }
+    }
+#endif
+  }
+  if (loop_args->loop_corner) {
+    p4est_iter_init_corner_from_volume (&(args->corner_args), args);
+  }
+
+  p4est_iter_init_loop_volume (args->loop_args, t, p4est, ghost_layer);
+}
+
+static void
+p4est_iter_reset_volume (p4est_iter_volume_args_t * args)
+{
+  int                 i, j;
+
+  for (i = 0; i < P4EST_DIM; i++) {
+    for (j = 0; j < P4EST_CHILDREN / 2; j++) {
+      p4est_iter_reset_face (&(args->face_args[i][j]));
+    }
+#ifdef P4_TO_P8
+    if (args->loop_args->loop_edge) {
+      for (j = 0; j < 2; j++) {
+        p8est_iter_reset_edge (&(args->edge_args[i][j]));
+      }
+    }
+#endif
+  }
+  if (args->loop_args->loop_corner) {
+    p4est_iter_reset_corner (&(args->corner_args));
+  }
+}
+
+/* when there is only a volume callback, there is no coordination necessary, so
+ * a simple loop is performed */
+static void
+p4est_volume_iterate_simple (p4est_t * p4est, p4est_ghost_t * ghost_layer,
+                             void *user_data, p4est_iter_volume_t iter_volume)
+{
+  p4est_topidx_t      t;
+  p4est_topidx_t      first_local_tree = p4est->first_local_tree;
+  p4est_topidx_t      last_local_tree = p4est->last_local_tree;
+  sc_array_t         *trees = p4est->trees;
+  p4est_tree_t       *tree;
+  size_t              si, n_quads;
+  sc_array_t         *quadrants;
+  p4est_iter_volume_info_t info;
+
+  info.p4est = p4est;
+  info.ghost_layer = ghost_layer;
+
+  for (t = first_local_tree; t <= last_local_tree; t++) {
+    info.treeid = t;
+    tree = p4est_tree_array_index (trees, t);
+    quadrants = &(tree->quadrants);
+    n_quads = quadrants->elem_count;
+    for (si = 0; si < n_quads; si++) {
+      info.quad = p4est_quadrant_array_index (quadrants, si);
+      info.quadid = si;
+      iter_volume (&info, user_data);
+    }
+  }
+}
+
+static void
+p4est_volume_iterate (p4est_iter_volume_args_t * args, void *user_data,
+                      p4est_iter_volume_t iter_volume,
+                      p4est_iter_face_t iter_face,
+#ifdef P4_TO_P8
+                      p8est_iter_edge_t iter_edge,
+#endif
+                      p4est_iter_corner_t iter_corner)
+{
+  const int           local = 0;
+  const int           ghost = 1;
+
+  int                 dir, side, type;
+
+  p4est_iter_loop_args_t *loop_args = args->loop_args;
+  int                 start_level = loop_args->level;
+  int                *Level = &(loop_args->level);
+  int                 start_idx2 = args->start_idx2;
+  int                *level_num = loop_args->level_num;
+  sc_array_t        **quadrants = loop_args->quadrants;
+  size_t            **zindex = loop_args->index;
+  size_t             *first_index = loop_args->first_index;
+  p4est_quadrant_t  **test = loop_args->test;
+  size_t             *count = loop_args->count;
+  int                *test_level = loop_args->test_level;
+  sc_array_t         *tier_rings = loop_args->tier_rings;
+  int                 quad_idx2;
+  sc_array_t          test_view;
+  p4est_iter_volume_info_t *info = &(args->info);
+  int                 level_idx2;
+  int                 refine;
+
+  /* level_idx2 moves us to the correct set of bounds within the index arrays
+   * for the level: it is a set of bounds because it includes all children at
+   * this level */
+  level_idx2 = start_level * P4EST_ITER_STRIDE;
+
+  /* start_idx2 gives the ancestor id at level for the search area,
+   * so quad_idx2 now gives the correct location in
+   * index[type] of the bounds of the search area */
+  quad_idx2 = level_idx2 + start_idx2;
+  for (type = local; type <= ghost; type++) {
+    first_index[type] = zindex[type][quad_idx2];
+    count[type] = zindex[type][quad_idx2 + 1] - first_index[type];
+  }
+
+  /* if ther are no local quadrants, nothing to be done */
+  if (!count[local]) {
+    return;
+  }
+
+  /* we think of the search tree as being rooted at start_level, so we can
+   * think the branch number at start_level as 0, even if it actually is not */
+  level_num[start_level] = 0;
+
+  for (;;) {
+
+    refine = 1;
+    /* for each type, get the first quadrant in the search area */
+    for (type = local; type <= ghost; type++) {
+      if (count[type]) {
+        test[type] = p4est_quadrant_array_index (quadrants[type],
+                                                 first_index[type]);
+        test_level[type] = (int) test[type]->level;
+        /* if the quadrant is the same size as the search area, we're done
+         * search */
+        if (test_level[type] == *Level) {
+          refine = 0;
+          P4EST_ASSERT (!count[type ^ 1]);
+          /* if the quadrant is local, we run the callback */
+          if (type == local) {
+            info->quad = test[type];
+            info->quadid = (p4est_locidx_t) first_index[type];
+            if (iter_volume != NULL) {
+              iter_volume (info, user_data);
+            }
+          }
+          /* proceed to the next search area on this level */
+          level_num[*Level]++;
+        }
+      }
+      else {
+        test[type] = NULL;
+        test_level[type] = -1;
+      }
+    }
+
+    if (refine) {
+      /* we need to refine, we take the search area and split it up, taking the
+       * indices for the refined search areas and placing them on the next tier in
+       * index[type] */
+      quad_idx2 = level_idx2 + P4EST_ITER_STRIDE;
+      for (type = local; type <= ghost; type++) {
+        sc_array_init_view (&test_view, quadrants[type],
+                            first_index[type], count[type]);
+        p4est_iter_tier_insert (&test_view, *Level, zindex[type] + quad_idx2,
+                                first_index[type], tier_rings, test[type]);
+      }
+
+      /* we descend to the first descendant search area and search there */
+      level_num[++(*Level)] = 0;
+      level_idx2 += P4EST_ITER_STRIDE;
+    }
+
+    for (;;) {
+      /* if we tried to advance the search area on start_level, we've completed
+       * the search */
+      if (level_num[start_level] > 0) {
+        return;
+      }
+
+      /* if we have tried to advance the search area past the number of
+       * descendants, that means that we have completed all of the branches on
+       * this level. we can now run the face_iterate for all of the faces between
+       * search areas on the level*/
+      if (level_num[*Level] == P4EST_CHILDREN) {
+        /* for each direction */
+        for (dir = 0; dir < P4EST_DIM; dir++) {
+          for (side = 0; side < P4EST_CHILDREN / 2; side++) {
+            p4est_iter_copy_indices (loop_args,
+                                     args->face_args[dir][side].start_idx2,
+                                     1, 2);
+            p4est_face_iterate (&(args->face_args[dir][side]), user_data,
+                                iter_face,
+#ifdef P4_TO_P8
+                                iter_edge,
+#endif
+                                iter_corner);
+          }
+        }
+#ifdef P4_TO_P8
+        /* if there is an edge or a corner callback, we need to use
+         * edge_iterate, so we set up the common corners and edge ids
+         * for all of the edges between the search areas */
+        if (loop_args->loop_edge) {
+          for (dir = 0; dir < P4EST_DIM; dir++) {
+            for (side = 0; side < 2; side++) {
+              p4est_iter_copy_indices (loop_args,
+                                       args->edge_args[dir][side].start_idx2,
+                                       1, 4);
+              p8est_edge_iterate (&(args->edge_args[dir][side]), user_data,
+                                  iter_edge, iter_corner);
+            }
+          }
+        }
+#endif
+        /* if there is a corner callback, we need to call corner_iterate on
+         * the corner in the middle of the search areas */
+        if (loop_args->loop_corner) {
+          p4est_iter_copy_indices (loop_args, args->corner_args.start_idx2, 1,
+                                   P4EST_CHILDREN);
+          p4est_corner_iterate (&(args->corner_args), user_data, iter_corner);
+        }
+        /* we are done at the level, so we go up a level and over a branch */
+        level_num[--(*Level)]++;
+        level_idx2 -= P4EST_ITER_STRIDE;
+      }
+      else {
+        /* quad_idx now gives the location in index[type] of the bounds
+         * of the current search area, from which we get the first quad
+         * and the count */
+        quad_idx2 = level_idx2 + level_num[*Level];
+        for (type = local; type <= ghost; type++) {
+          first_index[type] = zindex[type][quad_idx2];
+          count[type] = zindex[type][quad_idx2 + 1] - first_index[type];
+        }
+        if (!count[local]) {
+          /* if there are no local quadrants, we are done with this search area,
+           * and we advance to the next branch at this level */
+          level_num[*Level]++;
+        }
+        else {
+          /* otherwise we are done changing the search area */
+          break;
+        }
+      }
+    }
+  }
+  P4EST_ASSERT (*Level == start_level);
+}
+
+static int32_t     *
+p4est_iter_get_boundaries (p4est_t * p4est, p4est_topidx_t * last_run_tree,
+                           int remote)
+{
+  p4est_topidx_t      ti;
+  int                 i;
+  int                 rank = p4est->mpirank;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  sc_array_t         *trees = p4est->trees;
+  size_t              global_num_trees = trees->elem_count;
+  int32_t            *init = P4EST_ALLOC_ZERO (int32_t, global_num_trees);
+  int32_t            *owned = P4EST_ALLOC_ZERO (int32_t, global_num_trees);
+  int32_t             touch;
+  int32_t             mask;
+  p4est_topidx_t      t, nt, ot;
+  p4est_topidx_t      first_local_tree = p4est->first_local_tree;
+  p4est_topidx_t      last_local_tree = p4est->last_local_tree;
+  p4est_quadrant_t   *lq = &(p4est->global_first_position[rank]);
+  p4est_quadrant_t    temp;
+  p4est_quadrant_t   *uq = &(p4est->global_first_position[rank + 1]);
+  uint64_t            uqid;
+  p4est_quadrant_t   *tlq, *tuq;
+  int                 f, nf, c, c2, nc, oc;
+  p4est_topidx_t      corner;
+  p4est_topidx_t     *ttc = conn->tree_to_corner;
+  p4est_topidx_t     *ctt_offset = conn->ctt_offset;
+  p4est_topidx_t     *ctt = conn->corner_to_tree;
+  int8_t             *ctc = conn->corner_to_corner;
+#ifdef P4_TO_P8
+  int                 e, ne, oe;
+  int                 nc2;
+  p4est_topidx_t      edge;
+  p4est_topidx_t     *tte = conn->tree_to_edge;
+  p4est_topidx_t     *ett_offset = conn->ett_offset;
+  p4est_topidx_t     *ett = conn->edge_to_tree;
+  int8_t             *ete = conn->edge_to_edge;
+  int                 ref, set;
+  int                 this_o;
+#endif
+  p4est_topidx_t     *ttt = conn->tree_to_tree;
+  int8_t             *ttf = conn->tree_to_face;
+#ifndef P4_TO_P8
+  int                 corner_offset = 4;
+#else
+  int                 edge_offset = 6;
+  int                 corner_offset = 18;
+#endif
+  int                 o;
+
+  *last_run_tree = -1;
+
+  if (uq->p.which_tree > last_local_tree) {
+    uq = NULL;
+  }
+  else {
+    P4EST_ASSERT (uq->p.which_tree == last_local_tree);
+    uqid = p4est_quadrant_linear_id (uq, P4EST_QMAXLEVEL);
+    p4est_quadrant_set_morton (&temp, P4EST_QMAXLEVEL, uqid - 1);
+    uq = &temp;
+  }
+
+  for (t = first_local_tree; t <= last_local_tree; t++) {
+    if (!remote) {
+      tlq = (t == first_local_tree) ? lq : NULL;
+      tuq = (t == last_local_tree) ? uq : NULL;
+    }
+    else {
+      tlq = NULL;
+      tuq = NULL;
+    }
+    touch = p4est_find_range_boundaries (tlq, tuq, 0, NULL,
+#ifdef P4_TO_P8
+                                         NULL,
+#endif
+                                         NULL);
+    if (!touch) {
+      continue;
+    }
+    mask = 0x00000001;
+    for (f = 0; f < P4EST_FACES; f++, mask <<= 1) {
+      if ((touch & mask) && !(init[t] & mask)) {
+        nt = ttt[t * P4EST_FACES + f];
+        nf = (int) ttf[t * P4EST_FACES + f];
+        nf %= P4EST_FACES;
+        init[t] |= mask;
+        init[nt] |= (((int32_t) 1) << nf);
+        if (t > nt || ((t == nt) && (f >= nf))) {
+          owned[t] |= mask;
+          if (t > *last_run_tree) {
+            *last_run_tree = t;
+          }
+        }
+        else {
+          owned[nt] |= (((int32_t) 1) << nf);
+          if (nt > *last_run_tree) {
+            *last_run_tree = nt;
+          }
+        }
+      }
+    }
+#ifdef P4_TO_P8
+    for (e = 0; e < 12; e++, mask <<= 1) {
+      if ((touch & mask) && !(init[t] & mask)) {
+        if (tte != NULL) {
+          edge = tte[t * 12 + e];
+        }
+        else {
+          edge = -1;
+        }
+        if (edge >= 0) {
+          ot = -1;
+          oe = -1;
+          for (ti = ett_offset[edge]; ti < ett_offset[edge + 1]; ti++) {
+            nt = ett[ti];
+            ne = (int) ete[ti];
+            ne %= 12;
+            init[nt] |= (((int32_t) 1) << (ne + edge_offset));
+            if (nt > ot || ((nt == ot) && (ne >= oe))) {
+              ot = nt;
+              oe = ne;
+            }
+          }
+        }
+        else {
+          ot = t;
+          oe = e;
+          init[t] |= mask;
+          for (i = 0; i < 2; i++) {
+            c = p8est_edge_corners[e][0];
+            c2 = p8est_edge_corners[e][1];
+            f = p8est_edge_faces[e][i];
+            c = p8est_corner_face_corners[c][f];
+            c2 = p8est_corner_face_corners[c2][f];
+            nt = ttt[t * P4EST_FACES + f];
+            nf = (int) ttf[t * P4EST_FACES + f];
+            o = nf / P4EST_FACES;
+            nf %= P4EST_FACES;
+            if (t == nt && f == nf) {
+              continue;
+            }
+            ref = p8est_face_permutation_refs[f][nf];
+            set = p8est_face_permutation_sets[ref][o];
+            nc = p8est_face_permutations[set][c];
+            nc2 = p8est_face_permutations[set][c2];
+            nc = p8est_face_corners[nf][nc];
+            nc2 = p8est_face_corners[nf][nc2];
+            ne = p8est_child_corner_edges[nc][nc2];
+            init[nt] |= (((int32_t) 1) << (ne + edge_offset));
+            if (nt > ot || ((nt == ot) && (ne >= oe))) {
+              ot = nt;
+              oe = ne;
+            }
+          }
+        }
+        owned[ot] |= (((int32_t) 1) << (oe + edge_offset));
+        if (ot > *last_run_tree) {
+          *last_run_tree = ot;
+        }
+      }
+    }
+#endif
+    for (c = 0; c < P4EST_CHILDREN; c++, mask <<= 1) {
+      if ((touch & mask) && !(init[t] & mask)) {
+        if (ttc != NULL) {
+          corner = ttc[t * P4EST_CHILDREN + c];
+        }
+        else {
+          corner = -1;
+        }
+        if (corner >= 0) {
+          ot = -1;
+          oc = -1;
+          for (ti = ctt_offset[corner]; ti < ctt_offset[corner + 1]; ti++) {
+            nt = ctt[ti];
+            nc = (int) ctc[ti];
+            init[nt] |= (((int32_t) 1) << (nc + corner_offset));
+            if (nt > ot || ((nt == ot) && (nc >= oc))) {
+              ot = nt;
+              oc = nc;
+            }
+          }
+        }
+        else {
+          ot = t;
+          oc = c;
+          init[t] |= mask;
+          for (i = 0; i < P4EST_DIM; i++) {
+            f = p4est_corner_faces[c][i];
+            c2 = p4est_corner_face_corners[c][f];
+            nt = ttt[t * P4EST_FACES + f];
+            nf = (int) ttf[t * P4EST_FACES + f];
+            o = nf / P4EST_FACES;
+            nf %= P4EST_FACES;
+            if (t == nt && f == nf) {
+              continue;
+            }
+#ifndef P4_TO_P8
+            nc = p4est_face_corners[nf][(o == 0) ? c2 : 1 - c2];
+#else
+            ref = p8est_face_permutation_refs[f][nf];
+            set = p8est_face_permutation_sets[ref][o];
+            nc2 = p8est_face_permutations[set][c2];
+            nc = p8est_face_corners[nf][nc2];
+#endif
+            init[nt] |= (((int32_t) 1) << (nc + corner_offset));
+            if (nt > ot || ((nt == ot) && (nc >= oc))) {
+              ot = nt;
+              oc = nc;
+            }
+          }
+#ifdef P4_TO_P8
+          for (i = 0; i < 3; i++) {
+            e = p8est_corner_edges[c][i];
+            c2 = (p8est_edge_corners[e][0] == c) ? 0 : 1;
+            if (tte != NULL) {
+              edge = tte[t * 12 + e];
+            }
+            else {
+              edge = -1;
+            }
+            if (edge >= 0) {
+              for (ti = ett_offset[edge]; ti < ett_offset[edge + 1]; ti++) {
+                nt = ett[ti];
+                ne = (int) ete[ti];
+                this_o = ne / 12;
+                ne %= 12;
+                if (nt == t && ne == e) {
+                  break;
+                }
+              }
+              P4EST_ASSERT (ti < ett_offset[edge + 1]);
+              for (ti = ett_offset[edge]; ti < ett_offset[edge + 1]; ti++) {
+                nt = ett[ti];
+                ne = (int) ete[ti];
+                o = ne / 12;
+                ne %= 12;
+                nc = p8est_edge_corners[ne][(this_o == o) ? c2 : 1 - c2];
+                init[nt] |= (((int32_t) 1) << (nc + corner_offset));
+                if (nt > ot || ((nt == ot) && (nc >= oc))) {
+                  ot = nt;
+                  oc = nc;
+                }
+              }
+            }
+          }
+#endif
+        }
+        owned[ot] |= (((int32_t) 1) << (oc + corner_offset));
+        if (ot > *last_run_tree) {
+          *last_run_tree = ot;
+        }
+      }
+    }
+  }
+
+  P4EST_FREE (init);
+  return owned;
+}
+
+void
+p4est_iterate_ext (p4est_t * p4est, p4est_ghost_t * Ghost_layer,
+                   void *user_data, p4est_iter_volume_t iter_volume,
+                   p4est_iter_face_t iter_face,
+#ifdef P4_TO_P8
+                   p8est_iter_edge_t iter_edge,
+#endif
+                   p4est_iter_corner_t iter_corner, int remote)
+{
+  int                 f, c;
+  p4est_topidx_t      t;
+  p4est_ghost_t       empty_ghost_layer;
+  p4est_ghost_t      *ghost_layer;
+  sc_array_t         *trees = p4est->trees;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  size_t              global_num_trees = trees->elem_count;
+  p4est_iter_loop_args_t *loop_args;
+  p4est_iter_face_args_t face_args;
+#ifdef P4_TO_P8
+  int                 e;
+  p8est_iter_edge_args_t edge_args;
+#endif
+  p4est_iter_corner_args_t corner_args;
+  p4est_iter_volume_args_t args;
+  p4est_topidx_t      first_local_tree = p4est->first_local_tree;
+  p4est_topidx_t      last_local_tree = p4est->last_local_tree;
+  p4est_topidx_t      last_run_tree;
+  int32_t            *owned;
+  int32_t             mask, touch;
+
+  P4EST_ASSERT (p4est_is_valid (p4est));
+
+  if (p4est->first_local_tree < 0 ||
+      (iter_face == NULL && iter_corner == NULL &&
+#ifdef P4_TO_P8
+       iter_edge == NULL &&
+#endif
+       iter_volume == NULL)) {
+    return;
+  }
+
+  if (Ghost_layer == NULL) {
+    sc_array_init (&(empty_ghost_layer.ghosts), sizeof (p4est_quadrant_t));
+    empty_ghost_layer.tree_offsets = P4EST_ALLOC_ZERO (p4est_locidx_t,
+                                                       global_num_trees + 1);
+    empty_ghost_layer.proc_offsets = P4EST_ALLOC_ZERO (p4est_locidx_t,
+                                                       p4est->mpisize + 1);
+    ghost_layer = &empty_ghost_layer;
+  }
+  else {
+    ghost_layer = Ghost_layer;
+  }
+
+  /* simple loop if there is only a volume callback */
+  if (iter_face == NULL && iter_corner == NULL
+#ifdef P4_TO_P8
+      && iter_edge == NULL
+#endif
+    ) {
+    p4est_volume_iterate_simple (p4est, ghost_layer, user_data, iter_volume);
+    if (Ghost_layer == NULL) {
+      P4EST_FREE (empty_ghost_layer.tree_offsets);
+      P4EST_FREE (empty_ghost_layer.proc_offsets);
+    }
+    return;
+  }
+
+  /** initialize arrays that keep track of where we are in the search */
+  loop_args = p4est_iter_loop_args_new (conn,
+#ifdef P4_TO_P8
+                                        iter_edge,
+#endif
+                                        iter_corner, ghost_layer,
+                                        p4est->mpisize);
+
+  owned = p4est_iter_get_boundaries (p4est, &last_run_tree, remote);
+  last_run_tree = (last_run_tree < last_local_tree) ? last_local_tree :
+    last_run_tree;
+
+  /* start with the assumption that we only run on entities touches by the
+   * local processor's domain */
+  args.remote = remote;
+  face_args.remote = remote;
+#ifdef P4_TO_P8
+  edge_args.remote = remote;
+#endif
+  corner_args.remote = remote;
+
+  /** we have to loop over all trees and not just local trees because of the
+   * ghost layer */
+  for (t = first_local_tree; t <= last_run_tree; t++) {
+    if (t >= first_local_tree && t <= last_local_tree) {
+      p4est_iter_init_volume (&args, p4est, ghost_layer, loop_args, t);
+
+      p4est_volume_iterate (&args, user_data, iter_volume, iter_face,
+#ifdef P4_TO_P8
+                            iter_edge,
+#endif
+                            iter_corner);
+
+      p4est_iter_reset_volume (&args);
+    }
+
+    touch = owned[t];
+    if (!touch) {
+      continue;
+    }
+    mask = 0x00000001;
+    /* Now we need to run face_iterate on the faces between trees */
+    for (f = 0; f < 2 * P4EST_DIM; f++, mask <<= 1) {
+      if ((touch & mask) == 0) {
+        continue;
+      }
+      p4est_iter_init_face (&face_args, p4est, ghost_layer, loop_args, t, f);
+      p4est_face_iterate (&face_args, user_data, iter_face,
+#ifdef P4_TO_P8
+                          iter_edge,
+#endif
+                          iter_corner);
+      p4est_iter_reset_face (&face_args);
+    }
+
+    /* if there is an edge or a corner callback, we need to run
+     * edge_iterate on the edges between trees */
+#ifdef P4_TO_P8
+    if (loop_args->loop_edge) {
+      for (e = 0; e < 12; e++, mask <<= 1) {
+        if ((touch & mask) == 0) {
+          continue;
+        }
+        p8est_iter_init_edge (&edge_args, p4est, ghost_layer, loop_args, t,
+                              e);
+        p8est_edge_iterate (&edge_args, user_data, iter_edge, iter_corner);
+        p8est_iter_reset_edge (&edge_args);
+      }
+    }
+    else {
+      mask <<= 12;
+    }
+#endif
+
+    if (loop_args->loop_corner) {
+      for (c = 0; c < P4EST_CHILDREN; c++, mask <<= 1) {
+        if ((touch & mask) == 0) {
+          continue;
+        }
+        p4est_iter_init_corner (&corner_args, p4est, ghost_layer, loop_args,
+                                t, c);
+        p4est_corner_iterate (&corner_args, user_data, iter_corner);
+        p4est_iter_reset_corner (&corner_args);
+      }
+    }
+
+  }
+
+  if (Ghost_layer == NULL) {
+    P4EST_FREE (empty_ghost_layer.tree_offsets);
+    P4EST_FREE (empty_ghost_layer.proc_offsets);
+  }
+
+  P4EST_FREE (owned);
+  p4est_iter_loop_args_destroy (loop_args);
+}
+
+void
+p4est_iterate (p4est_t * p4est, p4est_ghost_t * Ghost_layer, void *user_data,
+               p4est_iter_volume_t iter_volume, p4est_iter_face_t iter_face,
+#ifdef P4_TO_P8
+               p8est_iter_edge_t iter_edge,
+#endif
+               p4est_iter_corner_t iter_corner)
+{
+  p4est_iterate_ext (p4est, Ghost_layer, user_data, iter_volume, iter_face,
+#ifdef P4_TO_P8
+                     iter_edge,
+#endif
+                     iter_corner, 0);
+}
diff --git a/src/p4est_iterate.h b/src/p4est_iterate.h
new file mode 100644
index 0000000..4d43a50
--- /dev/null
+++ b/src/p4est_iterate.h
@@ -0,0 +1,293 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_iterate.h
+ *
+ * Iteration over mesh topology via callbacks
+ *
+ * \ingroup p4est
+ */
+
+#ifndef P4EST_ITERATE_H
+#define P4EST_ITERATE_H
+
+#include <p4est.h>
+#include <p4est_ghost.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** The information that is available to the user-defined p4est_iter_volume_t
+ * callback function.
+ *
+ * \a treeid gives the index in \a p4est->trees of the tree to which
+ *    \a quad belongs.
+ * \a quadid gives the index of \a quad within \a tree's quadrants array.
+ */
+typedef struct p4est_iter_volume_info
+{
+  p4est_t            *p4est;
+  p4est_ghost_t      *ghost_layer;
+  p4est_quadrant_t   *quad;    /**< the quadrant of the callback */
+  p4est_locidx_t      quadid;  /**< id in \a quad's tree array (see
+                                    p4est_tree_t) */
+  p4est_topidx_t      treeid;  /**< the tree containing \a quad */
+}
+p4est_iter_volume_info_t;
+
+/** The prototype for a function that p4est_iterate will execute at every
+ * quadrant local to the current process.
+ * \param [in] info          information about a quadrant provided to the user
+ * \param [in,out] user_data the user context passed to p4est_iterate()
+ */
+typedef void        (*p4est_iter_volume_t) (p4est_iter_volume_info_t * info,
+                                            void *user_data);
+
+/** Information about one side of a face in the forest.
+ *
+ * If a \a quad is local
+ * (\a is_ghost is false), then its \a quadid indexes the tree's quadrant
+ * array; otherwise, it indexes the ghosts array. If the face is hanging, then
+ * the quadrants are listed in z-order.  If a quadrant should be present, but
+ * it is not included in the ghost layer, then quad = NULL, is_ghost is true,
+ * and quadid = -1.
+ */
+typedef struct p4est_iter_face_side
+{
+  p4est_topidx_t      treeid;          /**< the tree on this side */
+  int8_t              face;            /**< which quadrant side the face
+                                            touches */
+  int8_t              is_hanging;      /**< boolean: one full quad (0) or
+                                            two smaller quads (1) */
+  union p4est_iter_face_side_data
+  {
+    struct
+    {
+      int8_t              is_ghost;    /**< boolean: local (0) or ghost (1) */
+      p4est_quadrant_t   *quad;        /**< the actual quadrant */
+      p4est_locidx_t      quadid;      /**< index in tree or ghost array */
+    }
+    full; /**< if \a is_hanging = 0,
+               use is.full to access per-quadrant data */
+    struct
+    {
+      int8_t              is_ghost[2]; /**< boolean: local (0) or ghost (1) */
+      p4est_quadrant_t   *quad[2];     /**< the actual quadrant */
+      p4est_locidx_t      quadid[2];   /**< index in tree or ghost array */
+    }
+    hanging; /**< if \a is_hanging = 1,
+                  use is.hanging to access per-quadrant data */
+  }
+  is;
+}
+p4est_iter_face_side_t;
+
+/** The information that is available to the user-defined p4est_iter_face_t
+ * callback.
+ *
+ * The orientation is 0 if the face is within one tree; otherwise, it is the
+ * same as the orientation value between the two trees given in the
+ * connectivity.  If the face is on the outside boundary of the forest, then
+ * there is only one side.  If tree_boundary is false, the face is on the
+ * interior of a tree.  When tree_boundary is false, sides[0] contains the
+ * lowest z-order quadrant that touches the face.
+ * When tree_boundary is true, its value is P4EST_CONNECT_FACE.
+ */
+typedef struct p4est_iter_face_info
+{
+  p4est_t            *p4est;
+  p4est_ghost_t      *ghost_layer;
+  int8_t              orientation; /**< the orientation of the sides to each
+                                        other, as in the definition of
+                                        p4est_connectivity_t */
+  int8_t              tree_boundary; /**< boolean: interior face (0),
+                                          boundary face (1) */
+  sc_array_t          sides;    /* array of p4est_iter_face_side_t type */
+}
+p4est_iter_face_info_t;
+
+/** The prototype for a function that p4est_iterate will execute wherever two
+ * quadrants share a face: the face can be a 2:1 hanging face, it does not have
+ * to be conformal.
+ *
+ * \param [in] info          information about a quadrant provided to the user
+ * \param [in,out] user_data the user context passed to p4est_iterate()
+ *
+ * \note the forest must be face balanced for p4est_iterate() to execute a
+ * callback function on faces (see p4est_balance()).
+ */
+typedef void        (*p4est_iter_face_t) (p4est_iter_face_info_t * info,
+                                          void *user_data);
+
+/** Information about one side of a corner in the forest.  If a \a quad is local
+ * (\a is_ghost is false), then its \a quadid indexes the tree's quadrant array;
+ * otherwise, it indexes the ghosts array. If a quadrant should be present, but
+ * it is not included in the ghost layer, then quad = NULL, is_ghost is true,
+ * and quadid = -1.
+ *
+ * the \a faces field provides some additional information about the local
+ * topology: if side[i]->faces[j] == side[k]->faces[l], this indicates that
+ * there is a common face between these two sides of the corner.
+ */
+typedef struct p4est_iter_corner_side
+{
+  p4est_topidx_t      treeid;   /**< the tree that contains \a quad */
+  int8_t              corner;   /**< which of the quadrant's corners touches
+                                     this corner */
+  int8_t              is_ghost; /**< boolean: local (0) or ghost (1) */
+  p4est_quadrant_t   *quad;
+  p4est_locidx_t      quadid;   /**< the index in the tree or ghost array */
+  int8_t              faces[2]; /**< internal work data */
+}
+p4est_iter_corner_side_t;
+
+/** The information that is available to the user-defined p4est_iter_corner_t
+ * callback.
+ *
+ * If tree_boundary is false, the corner is on the interior of a tree.
+ * When tree_boundary is false, sides[0] contains the lowest z-order quadrant
+ * that touches the corner.
+ * When tree_boundary is true, its value is P4EST_CONNECT_FACE/CORNER
+ * depending on the location of the corner relative to the tree.
+ */
+typedef struct p4est_iter_corner_info
+{
+  p4est_t            *p4est;
+  p4est_ghost_t      *ghost_layer;
+  int8_t              tree_boundary; /**< boolean: interior face (0),
+                                           boundary face (1) */
+  sc_array_t          sides; /**< array of type p4est_iter_corner_side_t type */
+}
+p4est_iter_corner_info_t;
+
+/** The prototype for a function that p4est_iterate will execute wherever
+ * quadrants meet at a conformal corner
+ *
+ * i.e. the callback will not execute on a hanging corner.
+ *
+ * \param [in] info          information about a quadrant provided to the user
+ * \param [in,out] user_data the user context passed to p4est_iterate()
+ *
+ * \note the forest does not need to be corner balanced for p4est_iterate() to
+ * correctly execute a callback function at corners, only face balanced (see
+ * p4est_balance()).
+ */
+typedef void        (*p4est_iter_corner_t) (p4est_iter_corner_info_t * info,
+                                            void *user_data);
+
+/** Execute user supplied callbacks at every volume, face, and corner in the
+ * local forest.
+ *
+ * p4est_iterate executes the user-supplied callback functions at every
+ * volume, face, and corner in the local forest. The ghost_layer may be NULL.
+ * The \a user_data pointer is not touched by p4est_iterate, but is passed to
+ * each of the callbacks.  Any of the callbacks may be NULL.
+ * The callback functions are interspersed with each other, i.e. some face
+ * callbacks will occur between volume callbacks, and some corner callbacks
+ * will occur between face callbacks:
+ *
+ * 1) volume callbacks occur in the sorted Morton-index order.
+ * 2) a face callback is not executed until after the volume callbacks have
+ *    been executed for the quadrants that share it.
+ * 3) a corner callback is not executed until the face callbacks have been
+ *    executed for all faces that touch the corner.
+ * 4) it is not always the case that every face callback for a given quadrant
+ *    is executed before any of the corner callbacks.
+ * 5) callbacks are not executed at faces or corners that only involve ghost
+ *    quadrants, i.e. that are not adjacent in the local section of the
+ *    forest.
+ *
+ * \param[in] p4est          the forest
+ * \param[in] ghost_layer    optional: when not given, callbacks at the
+ *                           boundaries of the local partition cannot provide
+ *                           quadrant data about ghost quadrants: missing
+ *                           (p4est_quadrant_t *) pointers are set to NULL,
+ *                           missing indices are set to -1.
+ * \param[in,out] user_data  optional context to supply to each callback
+ * \param[in] iter_volume    callback function for every quadrant's interior
+ * \param[in] iter_face      callback function for every face between
+ *                           quadrants
+ * \param[in] iter_corner    callback function for every corner between
+ *                           quadrants
+ */
+void                p4est_iterate (p4est_t * p4est,
+                                   p4est_ghost_t * ghost_layer,
+                                   void *user_data,
+                                   p4est_iter_volume_t iter_volume,
+                                   p4est_iter_face_t iter_face,
+                                   p4est_iter_corner_t iter_corner);
+
+/** Return a pointer to a iter_corner_side array element indexed by a int.
+ */
+/*@unused@*/
+static inline p4est_iter_corner_side_t *
+p4est_iter_cside_array_index_int (sc_array_t * array, int it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_iter_corner_side_t));
+  P4EST_ASSERT (it >= 0 && (size_t) it < array->elem_count);
+
+  return (p4est_iter_corner_side_t *)
+    (array->array + sizeof (p4est_iter_corner_side_t) * it);
+}
+
+/** Return a pointer to a iter_corner_side array element indexed by a size_t.
+ */
+/*@unused@*/
+static inline p4est_iter_corner_side_t *
+p4est_iter_cside_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_iter_corner_side_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p4est_iter_corner_side_t *)
+    (array->array + sizeof (p4est_iter_corner_side_t) * it);
+}
+
+/** Return a pointer to a iter_face_side array element indexed by a int.
+ */
+/*@unused@*/
+static inline p4est_iter_face_side_t *
+p4est_iter_fside_array_index_int (sc_array_t * array, int it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_iter_face_side_t));
+  P4EST_ASSERT (it >= 0 && (size_t) it < array->elem_count);
+
+  return (p4est_iter_face_side_t *)
+    (array->array + sizeof (p4est_iter_face_side_t) * it);
+}
+
+/** Return a pointer to a iter_face_side array element indexed by a size_t.
+ */
+/*@unused@*/
+static inline p4est_iter_face_side_t *
+p4est_iter_fside_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_iter_face_side_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p4est_iter_face_side_t *)
+    (array->array + sizeof (p4est_iter_face_side_t) * it);
+}
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_ITERATE_H */
diff --git a/src/p4est_lnodes.c b/src/p4est_lnodes.c
new file mode 100644
index 0000000..ffe79d2
--- /dev/null
+++ b/src/p4est_lnodes.c
@@ -0,0 +1,3482 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_communication.h>
+#include <p4est_extended.h>
+#include <p4est_ghost.h>
+#include <p4est_lnodes.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_communication.h>
+#include <p8est_extended.h>
+#include <p8est_ghost.h>
+#include <p8est_lnodes.h>
+#endif
+
+#ifndef P4_TO_P8
+#define P4EST_LN_C_OFFSET 4
+#else
+#define P8EST_LN_E_OFFSET 6
+#define P4EST_LN_C_OFFSET 18
+#endif
+
+static int
+p4est_locidx_offset_compare (const void *key, const void *elem)
+{
+  const p4est_locidx_t *start = (p4est_locidx_t *) elem;
+  const p4est_locidx_t *stop = (p4est_locidx_t *) start + 1;
+  int                 comp = p4est_locidx_compare (key, start);
+  if (comp < 0) {
+    return -1;
+  }
+  comp = p4est_locidx_compare (key, stop);
+  if (comp >= 0) {
+    return 1;
+  }
+  return 0;
+}
+
+/** dep: dependent quads and processes.
+ * Suppose quadrants q0, q1, q2, and q3 share the same face neighbor p.
+ *
+ *       _______ o       +____________
+ *      /       /        /            /
+ *     /______ /| q3    /            /|
+ *    /       / |      /            / |
+ *   /______ /| /     /___________ /  |
+ *   |      | |/| q1  |           |   |
+ *   |  q2  | / |     |           |   |
+ *   |______|/| /     |     p     |   /
+ *   |      | |/      |           |  /
+ *   |  q0  | /       |           | /
+ *   |______|/        |___________|/
+ *
+ * Even though quads q0, q1, and q2 do not touch the corner marked by "+",
+ * they share the node that is created there.  When the callback that creates
+ * that node is run, it needs to know about all quads that share the node.
+ * Quadrant q3 has one dep: during the face callback between face p and the q
+ * faces, the dep associated with the marked corner "o" will have one of its
+ * face values record the presence of q0, on edge value record the presence of
+ * q1, and one edge value record the presence of q2.  If the quads are local,
+ * their indices are stored; if they are ghosts, their owner is encoded as
+ * -(proc + 3).  This allows -1 to stand for "uninitialized", and -2 to stand
+ *  for "no dependencies".
+ */
+
+/* new idea: quad index */
+typedef struct p4est_lnodes_dep
+{
+  p4est_locidx_t      face[P4EST_DIM];
+#ifdef P4_TO_P8
+  p4est_locidx_t      edge[P4EST_DIM];
+#endif
+}
+p4est_lnodes_dep_t;
+
+/** buf_info: encodes/decodes the transmission of node information.
+ * share_offset and share_count index into the inode_sharers array
+ * for a list of all processes that share the nodes (local process included).
+ */
+typedef struct p4est_lnodes_buf_info
+{
+  int8_t              type;     /* which nodes it shares */
+  int8_t              send_sharers;     /* whether the sharers are included in
+                                           the message */
+  p4est_locidx_t      first_index;      /* inodes array, first node to/from */
+  p4est_locidx_t      share_offset;
+  int8_t              share_count;
+}
+p4est_lnodes_buf_info_t;
+
+typedef struct p4est_lnodes_data
+{
+  p4est_lnodes_dep_t *local_dep;        /* num local quads */
+  p4est_lnodes_dep_t *ghost_dep;        /* num ghost quads */
+  p4est_locidx_t     *local_elem_nodes; /* num local quads * nodes per q */
+  p4est_locidx_t     *poff;     /* mpisize + 1 */
+  sc_array_t         *inodes;   /* 2 * p4est_locidx_t */
+  sc_array_t         *inode_sharers;    /* int */
+  sc_array_t         *send_buf_info;    /* one for each proc: type buf_info_t */
+  sc_array_t         *recv_buf_info;    /* one for each proc: type buf_info_t */
+  p4est_lnodes_code_t *face_codes;
+  int                 nodes_per_elem;
+  int                 nodes_per_volume;
+  int                *volume_nodes;
+  int                 nodes_per_face;
+  int                *face_nodes[P4EST_FACES];
+#ifdef P4_TO_P8
+  int                 nodes_per_edge;
+  int                *edge_nodes[P8EST_EDGES];
+#endif
+  int                 nodes_per_corner;
+  int                *corner_nodes[P4EST_CHILDREN];
+  sc_array_t          send_requests;
+  sc_array_t         *send_buf;
+  sc_array_t         *touching_procs;
+  sc_array_t         *all_procs;
+}
+p4est_lnodes_data_t;
+
+static inline int
+fside_get_fields (p4est_iter_face_side_t * fside, int *is_hanging,
+                  p4est_topidx_t * tid, int *f, int8_t ** is_ghost,
+                  p4est_locidx_t ** quadid, p4est_quadrant_t *** quad)
+{
+  int                 limit;
+
+  *is_hanging = fside->is_hanging;
+  *tid = fside->treeid;
+  *f = (int) fside->face;
+  if (fside->is_hanging) {
+    limit = P4EST_HALF;
+    *is_ghost = fside->is.hanging.is_ghost;
+    *quadid = fside->is.hanging.quadid;
+    *quad = fside->is.hanging.quad;
+  }
+  else {
+    limit = 1;
+    *is_ghost = &fside->is.full.is_ghost;
+    *quadid = &fside->is.full.quadid;
+    *quad = &fside->is.full.quad;
+  }
+
+  return limit;
+}
+
+/** lnodes_face_simple_callback: runs even if there are no face nodes.  If a
+ * side of the face is not hanging, then there are no other quadrants that are
+ * facewise dependent on its corner or edge nodes.  If a side of the face is
+ * hanging, we set up the facewise dep values.  We also update the face_codes
+ * for local quads.  Store a list of all touching processors */
+static void
+p4est_lnodes_face_simple_callback (p4est_iter_face_info_t * info, void *Data)
+{
+  int                 i, f, fdir, limit, cid, xind, *ip;
+  sc_array_t         *sides = &(info->sides);
+  size_t              zz, count = sides->elem_count;
+  p4est_lnodes_data_t *data = (p4est_lnodes_data_t *) Data;
+  p4est_iter_face_side_t *fside;
+  sc_array_t         *trees = info->p4est->trees;
+  p4est_tree_t       *tree;
+  p4est_topidx_t      tid;
+  sc_array_t         *touching_procs = data->touching_procs;
+  sc_array_t          proc_offsets;
+  p4est_lnodes_dep_t *local_dep = data->local_dep;
+  p4est_lnodes_dep_t *ghost_dep = data->ghost_dep;
+  p4est_lnodes_dep_t *dep;
+  p4est_locidx_t      quadrants_offset;
+  int                 rank = info->p4est->mpirank;
+  p4est_lnodes_code_t *face_codes = data->face_codes;
+  int8_t             *is_ghost;
+  p4est_locidx_t     *quadid;
+  p4est_quadrant_t  **quad;
+  int                 is_hanging;
+  int                 procs[P4EST_HALF];
+  p4est_locidx_t      qid[P4EST_HALF];
+#ifdef P4_TO_P8
+  int                 j;
+#endif
+  int                 k, c;
+  p4est_quadrant_t    tempq;
+
+  P4EST_ASSERT (touching_procs->elem_size == sizeof (int));
+  sc_array_truncate (touching_procs);
+  /* even though the original is size mpisize+1, proc_offsets uses
+   * p4est_locidx_offset_compare, and we don't want to read past the end of the
+   * array */
+  sc_array_init_data (&proc_offsets, info->ghost_layer->proc_offsets,
+                      sizeof (p4est_locidx_t), (size_t) info->p4est->mpisize);
+
+  for (zz = 0; zz < count; zz++) {
+    fside = p4est_iter_fside_array_index (sides, zz);
+    limit = fside_get_fields (fside, &is_hanging, &tid, &f, &is_ghost,
+                              &quadid, &quad);
+    fdir = f / 2;
+    tree = p4est_tree_array_index (trees, tid);
+    quadrants_offset = tree->quadrants_offset;
+    k = -1;
+    for (i = 0; i < limit; i++) {
+      qid[i] = quadid[i];
+      if (qid[i] < 0) {
+        P4EST_ASSERT (limit == P4EST_HALF);
+        if (k < 0) {
+          for (k = 0; k < P4EST_HALF; k++) {
+            if (quadid[k] >= 0) {
+              P4EST_ASSERT (quad[k]);
+              break;
+            }
+          }
+        }
+        P4EST_ASSERT (k >= 0 && k < P4EST_HALF);
+        c = p4est_face_corners[f][i];
+        p4est_quadrant_sibling (quad[k], &tempq, c);
+        procs[i] = p4est_comm_find_owner (info->p4est, tid,
+                                          &tempq, info->p4est->mpirank);
+        P4EST_ASSERT (procs[i] >= 0 && procs[i] != rank);
+        ip = (int *) sc_array_push (touching_procs);
+        *ip = procs[i];
+      }
+      else if (is_ghost[i]) {
+        procs[i] = (int) sc_array_bsearch (&proc_offsets, &(qid[i]),
+                                           p4est_locidx_offset_compare);
+        P4EST_ASSERT (procs[i] >= 0 && procs[i] != rank);
+        ip = (int *) sc_array_push (touching_procs);
+        *ip = procs[i];
+      }
+      else {
+        qid[i] += quadrants_offset;
+        procs[i] = rank;
+        /* update face code */
+        if (is_hanging) {
+          face_codes[qid[i]] |= ((p4est_lnodes_code_t)
+                                 p4est_face_corners[f][i]);
+          face_codes[qid[i]] |= ((p4est_lnodes_code_t)
+                                 1 << (P4EST_DIM + f / 2));
+        }
+      }
+    }
+    if (!data->nodes_per_corner &&
+#ifdef P4_TO_P8
+        !data->nodes_per_edge &&
+#endif
+        1) {
+      continue;
+    }
+    for (i = 0; i < limit; i++) {
+      dep = !is_ghost[i] ? &(local_dep[qid[i]]) : &(ghost_dep[qid[i]]);
+      if (is_hanging) {
+#ifdef P4_TO_P8
+        int                 ndir[2];
+
+        ndir[0] = SC_MIN (((fdir + 1) % 3), ((fdir + 2) % 3));
+        ndir[1] = SC_MAX (((fdir + 1) % 3), ((fdir + 2) % 3));
+
+        for (j = 0; j < 2; j++) {
+          xind = i ^ (j + 1);
+          if (is_ghost[xind]) {
+            dep->edge[ndir[j]] = -((p4est_locidx_t) procs[xind] + 3);
+          }
+          else {
+            dep->edge[ndir[j]] = qid[xind];
+          }
+        }
+#endif
+        xind = i ^ (P4EST_HALF - 1);
+        if (is_ghost[xind]) {
+          dep->face[fdir] = -((p4est_locidx_t) procs[xind] + 3);
+        }
+        else {
+          dep->face[fdir] = qid[xind];
+        }
+      }
+      else {
+        cid = p4est_quadrant_child_id (quad[i]);
+        if (p4est_corner_face_corners[cid][f] >= 0) {
+          dep->face[fdir] = -2;
+        }
+      }
+    }
+  }
+}
+
+#ifdef P4_TO_P8
+
+static inline int
+eside_get_fields (p8est_iter_edge_side_t * eside, int *is_hanging,
+                  p4est_topidx_t * tid, int *e, int *o, int8_t ** is_ghost,
+                  p4est_locidx_t ** quadid, p4est_quadrant_t *** quad)
+{
+  int                 limit;
+
+  *is_hanging = eside->is_hanging;
+  *tid = eside->treeid;
+  *e = (int) eside->edge;
+  *o = (int) eside->orientation;
+  if (eside->is_hanging) {
+    limit = 2;
+    *is_ghost = eside->is.hanging.is_ghost;
+    *quadid = eside->is.hanging.quadid;
+    *quad = eside->is.hanging.quad;
+  }
+  else {
+    limit = 1;
+    *is_ghost = &eside->is.full.is_ghost;
+    *quadid = &eside->is.full.quadid;
+    *quad = &eside->is.full.quad;
+  }
+
+  return limit;
+}
+
+/** lnodes_edge_simple_callback: runs even if there are no edge nodes.  If a
+ * side of the face is not hanging, then there are no other quadrants that are
+ * facewise dependent on its corner or edge nodes.  If a side of the face is
+ * hanging, we set up the facewise dep values.  We also update the face_codes
+ * for local quads.  Store a list of all touching procs.  return whether there
+ * is a local touching quadrant.
+ */
+static int
+p8est_lnodes_edge_simple_callback (p8est_iter_edge_info_t * info, void *Data)
+{
+  int                 i, limit, e, edir, cid, *ip;
+  sc_array_t         *sides = &(info->sides);
+  size_t              zz, count = sides->elem_count;
+  p4est_lnodes_data_t *data = (p4est_lnodes_data_t *) Data;
+  p8est_iter_edge_side_t *eside;
+  sc_array_t         *trees = info->p4est->trees;
+  p4est_tree_t       *tree;
+  p4est_topidx_t      tid;
+  sc_array_t          proc_offsets;
+  sc_array_t         *touching_procs = data->touching_procs;
+  p4est_lnodes_dep_t *local_dep = data->local_dep;
+  p4est_lnodes_dep_t *ghost_dep = data->ghost_dep;
+  p4est_lnodes_dep_t *dep;
+  int8_t             *is_ghost;
+  int                 procs[2];
+  int                 rank = info->p4est->mpirank;
+  p4est_locidx_t     *quadid;
+  p4est_quadrant_t  **quad;
+  p4est_locidx_t      qid[2];
+  p4est_locidx_t      quadrants_offset;
+  p4est_lnodes_code_t *face_codes = data->face_codes;
+  int                 is_hanging, o, has_local = 0, c;
+  p4est_quadrant_t    tempq;
+
+  P4EST_ASSERT (touching_procs->elem_size == sizeof (int));
+  sc_array_truncate (touching_procs);
+  /* even though the original is size mpisize+1, proc_offsets uses
+   * p4est_locidx_offset_compare, and we don't want to read past the end of the
+   * array */
+  sc_array_init_data (&proc_offsets, info->ghost_layer->proc_offsets,
+                      sizeof (p4est_locidx_t), (size_t) info->p4est->mpisize);
+
+  for (zz = 0; zz < count; zz++) {
+    eside = p8est_iter_eside_array_index (sides, zz);
+    limit = eside_get_fields (eside, &is_hanging, &tid, &e, &o, &is_ghost,
+                              &quadid, &quad);
+    edir = e / 4;
+    tree = p4est_tree_array_index (trees, tid);
+    quadrants_offset = tree->quadrants_offset;
+    for (i = 0; i < limit; i++) {
+      qid[i] = quadid[i];
+      if (qid[i] < 0) {
+        if (limit == 2 && quadid[i ^ 1] >= 0) {
+          P4EST_ASSERT (quad[i ^ 1]);
+          c = p8est_edge_corners[e][i];
+          p4est_quadrant_sibling (quad[i ^ 1], &tempq, c);
+          procs[i] = p4est_comm_find_owner (info->p4est, tid,
+                                            &tempq, info->p4est->mpirank);
+          P4EST_ASSERT (procs[i] >= 0 && procs[i] != rank);
+          ip = (int *) sc_array_push (touching_procs);
+          *ip = procs[i];
+        }
+      }
+      else if (is_ghost[i]) {
+        procs[i] = (int) sc_array_bsearch (&proc_offsets, &(qid[i]),
+                                           p4est_locidx_offset_compare);
+        P4EST_ASSERT (procs[i] >= 0 && procs[i] != rank);
+        ip = (int *) sc_array_push (touching_procs);
+        *ip = procs[i];
+      }
+      else {
+        has_local = 1;
+        qid[i] += quadrants_offset;
+        procs[i] = rank;
+        if (is_hanging) {
+          /* update face code */
+          face_codes[qid[i]] |=
+            ((p4est_lnodes_code_t) p8est_edge_corners[e][i]);
+          face_codes[qid[i]] |= ((p4est_lnodes_code_t) 1 << (6 + e / 4));
+        }
+      }
+    }
+    for (i = 0; i < limit; i++) {
+      if (qid[i] < 0) {
+        continue;
+      }
+      dep = !is_ghost[i] ? &(local_dep[qid[i]]) : &(ghost_dep[qid[i]]);
+      if (is_hanging) {
+        if (!has_local && qid[i ^ 1] < 0) {
+          dep->edge[edir] = -1;
+        }
+        else if (is_ghost[i ^ 1]) {
+          dep->edge[edir] = -((p4est_locidx_t) procs[i ^ 1] + 3);
+        }
+        else {
+          dep->edge[edir] = qid[i ^ 1];
+        }
+      }
+      else {
+        cid = p4est_quadrant_child_id (quad[i]);
+        if (p8est_edge_corners[e][0] == cid ||
+            p8est_edge_corners[e][1] == cid) {
+          dep->edge[edir] = -2;
+        }
+      }
+    }
+  }
+
+  return has_local;
+}
+
+static void
+p8est_lnodes_edge_simple_callback_void (p8est_iter_edge_info_t * info,
+                                        void *Data)
+{
+  (void) p8est_lnodes_edge_simple_callback (info, Data);
+}
+#endif
+
+static inline void
+cside_get_fields (p4est_iter_corner_side_t * cside,
+                  p4est_topidx_t * tid, int *c, int8_t * is_ghost,
+                  p4est_locidx_t * quadid, p4est_quadrant_t ** quad)
+{
+  *tid = cside->treeid;
+  *c = cside->corner;
+  *is_ghost = cside->is_ghost;
+  *quadid = cside->quadid;
+  *quad = cside->quad;
+}
+
+/** Once we have found the quadrant (\a q, \a tid, \a type) that owns a set of
+ * nodes, push the info describing the owner quadrant on the appropriate
+ * send/recv lists.
+ */
+static inline void
+p4est_lnodes_push_binfo (sc_array_t * touch, sc_array_t * all,
+                         sc_array_t * send, sc_array_t * recv,
+                         sc_array_t * share, int owner, int rank,
+                         int mpisize, int is_remote,
+                         int8_t type, p4est_locidx_t nin)
+{
+  size_t              zz, count = all->elem_count;
+  int                *ip, proc;
+  p4est_lnodes_buf_info_t *binfo;
+  int8_t              scount = -1;
+  p4est_locidx_t      offset = (p4est_locidx_t) share->elem_count;
+
+  if (!is_remote) {
+    ip = (int *) sc_array_push (share);
+    *ip = rank;
+    scount = (int8_t) (count + 1);
+  }
+  for (zz = 0; zz < count; zz++) {
+    proc = *((int *) sc_array_index (all, zz));
+    if (!is_remote) {
+      ip = (int *) sc_array_push (share);
+      *ip = proc;
+    }
+    if (owner == rank) {
+      P4EST_ASSERT (proc != rank);
+      P4EST_ASSERT (!is_remote);
+      P4EST_ASSERT (0 <= proc && proc < mpisize);
+      binfo = (p4est_lnodes_buf_info_t *) sc_array_push (&(send[proc]));
+      binfo->send_sharers = 1;
+      if (touch == NULL ||
+          sc_array_bsearch (touch, &proc, sc_int_compare) >= 0) {
+        binfo->send_sharers = 0;
+      }
+    }
+    else if (proc == owner) {
+      P4EST_ASSERT (0 <= proc && proc < mpisize);
+      binfo = (p4est_lnodes_buf_info_t *) sc_array_push (&(recv[proc]));
+      if (!is_remote) {
+        binfo->send_sharers = 0;
+      }
+      else {
+        binfo->send_sharers = 1;
+      }
+    }
+    else {
+      continue;
+    }
+    binfo->type = type;
+    binfo->first_index = nin;
+    if (!is_remote) {
+      binfo->share_offset = offset;
+      binfo->share_count = scount;
+    }
+    else {
+      binfo->share_offset = -1;
+      binfo->share_count = -1;
+    }
+  }
+}
+
+/** p4est_lnodes_missing_proc_corner: figure out processors that may share a
+ * corner node remotely
+ */
+static int
+p4est_lnodes_missing_proc_corner (p4est_iter_corner_info_t * info, int side,
+                                  int b)
+{
+  sc_array_t         *sides = &(info->sides);
+  int                 i, nsides = (int) sides->elem_count;
+  p4est_iter_corner_side_t *thisside = p4est_iter_cside_array_index_int
+    (sides, side);
+  p4est_iter_corner_side_t *cside;
+  p4est_quadrant_t   *q, tempq;
+  int                 l, j, f, fc, c2, nproc, key, test;
+  int                 c = thisside->corner;
+#ifdef P4_TO_P8
+  int                 e;
+#endif
+
+  q = thisside->quad;
+  P4EST_ASSERT (q != NULL);
+  l = q->level;
+  if (l == 0) {
+    return -1;
+  }
+
+  if (b < P4EST_DIM) {
+    key = thisside->faces[b];
+    f = p4est_corner_faces[c][b];
+    fc = p4est_corner_face_corners[c][f];
+    c2 = p4est_face_corners[f][fc ^ (P4EST_HALF - 1)];
+  }
+#ifndef P4_TO_P8
+  else {
+    key = -1;
+    c2 = -1;
+    SC_ABORT_NOT_REACHED ();
+  }
+#else
+  else {
+    key = thisside->edges[b - 3];
+    e = p8est_corner_edges[c][b - 3];
+    if (p8est_edge_corners[e][0] == c) {
+      c2 = p8est_edge_corners[e][1];
+    }
+    else {
+      c2 = p8est_edge_corners[e][0];
+    }
+  }
+#endif
+  p4est_quadrant_sibling (q, &tempq, c2);
+
+  for (i = 0; i < nsides; i++) {
+    if (i == side) {
+      continue;
+    }
+    cside = p4est_iter_cside_array_index_int (sides, i);
+    for (j = 0; j < P4EST_DIM; j++) {
+      test = cside->faces[j];
+#ifdef P4_TO_P8
+      if (b >= P4EST_DIM) {
+        test = cside->edges[j];
+      }
+#endif
+      if (test == key) {
+        P4EST_ASSERT (cside->quad != NULL);
+        if (cside->quad->level < l) {
+          P4EST_ASSERT (cside->quad->level == l - 1);
+          nproc =
+            p4est_comm_find_owner (info->p4est, thisside->treeid, &tempq,
+                                   info->p4est->mpirank);
+          P4EST_ASSERT (nproc >= 0);
+          return nproc;
+        }
+      }
+    }
+  }
+  return -1;
+}
+
+/* p4est_lnodes_corner_callback:
+ *
+ * Create a new independent node at a corner.  The quad in the first side is
+ * the owner: determine the owning proc.  If the node isn't remote, compute
+ * all processes that share the node.  For every local quad that shares the
+ * node, point local_elem_nodes at the new node.  If the node is locally
+ * owned, add info describing the node to the send buffer of all processes
+ * that need the node.  If the node is not locally owned, add info describing
+ * the node to the receive buffer of the owner.
+ */
+static void
+p4est_lnodes_corner_callback (p4est_iter_corner_info_t * info, void *Data)
+{
+  int                 i, j, limit;
+  sc_array_t         *sides = &(info->sides);
+  size_t              zz, count = sides->elem_count;
+  p4est_lnodes_data_t *data = (p4est_lnodes_data_t *) Data;
+  p4est_iter_corner_side_t *cside, *owner_cside;
+  sc_array_t         *inodes = data->inodes;
+  p4est_locidx_t     *inode, *lp;
+  sc_array_t         *inode_sharers = data->inode_sharers;
+  p4est_lnodes_dep_t *local_dep = data->local_dep;
+  p4est_lnodes_dep_t *ghost_dep = data->ghost_dep;
+  p4est_lnodes_dep_t *dep;
+  p4est_locidx_t     *local_elem_nodes = data->local_elem_nodes;
+  sc_array_t         *send_buf_info = data->send_buf_info;
+  sc_array_t         *recv_buf_info = data->recv_buf_info;
+  sc_array_t         *touching_procs = data->touching_procs;
+  sc_array_t         *all_procs = data->all_procs;
+  int                *ip;
+  p4est_topidx_t      tid, owner_tid;
+  sc_array_t         *trees = info->p4est->trees;
+  p4est_tree_t       *tree;
+  p4est_ghost_t      *ghost_layer = info->ghost_layer;
+  sc_array_t          proc_offsets;
+  p4est_locidx_t      qid, owner_qid, nqid;
+  p4est_locidx_t      num_inodes = (p4est_locidx_t) inodes->elem_count;
+  int                 npc = data->nodes_per_corner;
+  int8_t              is_ghost, owner_is_ghost;
+  p4est_locidx_t      nid;
+  int                 proc, owner_proc, nproc;
+  int                 rank = info->p4est->mpirank;
+  int                 c, owner_c;
+  p4est_quadrant_t    ownq, tempq, tempr;
+  p4est_quadrant_t   *q, *owner_q;
+  int               **corner_nodes = data->corner_nodes;
+  int                 nodes_per_elem = data->nodes_per_elem;
+  p4est_locidx_t      quadrants_offset;
+  int8_t              type;
+  p4est_connectivity_t *conn = info->p4est->connectivity;
+  int                 is_remote;
+  int                 has_local;
+
+  /* even though the original is size mpisize+1, proc_offsets uses
+   * p4est_locidx_offset_compare, and we don't want to read past the end of the
+   * array */
+  sc_array_init_data (&proc_offsets, ghost_layer->proc_offsets,
+                      sizeof (p4est_locidx_t), (size_t) info->p4est->mpisize);
+
+  sc_array_truncate (touching_procs);
+  sc_array_truncate (all_procs);
+
+  /* figure out which proc owns the node */
+  owner_cside = p4est_iter_cside_array_index (sides, 0);
+  cside_get_fields (owner_cside, &owner_tid, &owner_c, &owner_is_ghost,
+                    &owner_qid, &owner_q);
+  if (owner_q == NULL) {
+    p4est_qcoord_t      x, y, h;
+#ifdef P4_TO_P8
+    p4est_qcoord_t      l, z;
+#endif
+    /* if this is a remote node, we don't have a quad available for
+     * determining ownership, so we have to create it */
+    P4EST_ASSERT (count > 1);
+    cside = NULL;
+    for (zz = 1; zz < count; zz++) {
+      /* find a nonempty side */
+      cside = p4est_iter_cside_array_index (sides, zz);
+      if (cside->quad) {
+        break;
+      }
+    }
+    P4EST_ASSERT (zz < count);
+    cside_get_fields (cside, &tid, &c, &is_ghost, &qid, &q);
+    p4est_quadrant_corner_descendant (q, &tempr, c, P4EST_QMAXLEVEL);
+    q = &tempr;
+    P4EST_ASSERT (p4est_quadrant_child_id (q) == c);
+    /* we want the coordinates of the common corner as the owning process sees
+     * them.  transform the quad across the corner: the status of the transformed
+     * quad tells us how to proceed */
+    p4est_quadrant_corner_neighbor (q, c, &tempq);
+    if (p4est_quadrant_is_inside_root (&tempq)) {
+      /* inside the root, only one set of coordinates */
+      h = P4EST_QUADRANT_LEN (q->level);
+      x = q->x + h * (c & 1);
+      y = q->y + h * ((c & 2) >> 1);
+#ifdef P4_TO_P8
+      z = q->z + h * ((c & 4) >> 2);
+#endif
+    }
+    else if (p4est_quadrant_is_outside_corner (&tempq)) {
+      /* outside a corner, trivially set the coordinates to the appropriate
+       * corner */
+      h = P4EST_QUADRANT_LEN (0);
+      x = h * (owner_c & 1);
+      y = h * ((owner_c & 2) >> 1);
+#ifdef P4_TO_P8
+      z = h * ((owner_c & 4) >> 2);
+#endif
+    }
+#ifdef P4_TO_P8
+    else if (p8est_quadrant_is_outside_edge (&tempq)) {
+      /* outside an edge: use some knowledge about how p4est_iterate orders
+       * the sides around a corner that is in the middle of an edge */
+      int                 owner_e, owner_c2, e, c2, this_c, this_c2;
+
+      P4EST_ASSERT (count % 2 == 0);
+      /* side[count/2] should be on the same edge as side[0] */
+      cside = p4est_iter_cside_array_index (sides, count / 2);
+      P4EST_ASSERT (cside->treeid == owner_tid);
+      P4EST_ASSERT (p8est_child_corner_edges[owner_c][cside->corner] >= 0);
+      /* we now have two corners, which determines the edge from the owner's
+       * point of view */
+      owner_c2 = cside->corner;
+      owner_e = p8est_child_corner_edges[owner_c][owner_c2];
+      /* side[zz] is on the same edge as side[zz +- count / 2] */
+      if (zz < count / 2) {
+        cside = p4est_iter_cside_array_index (sides, zz + count / 2);
+        /* the order of this_c and this_c2 reflects the orientation of the
+         * edge that the corners touch */
+        this_c = c;
+        this_c2 = cside->corner;
+      }
+      else {
+        cside = p4est_iter_cside_array_index (sides, zz - count / 2);
+        /* the order of this_c and this_c2 reflects the orientation of the
+         * edge that the corners touch */
+        this_c = cside->corner;
+        this_c2 = c;
+      }
+      /* we now have two corners, which determines the edge from zz's point of
+       * view */
+      P4EST_ASSERT (cside->treeid == tid);
+      c2 = cside->corner;
+      e = p8est_child_corner_edges[c][c2];
+      P4EST_ASSERT (e >= 0);
+      h = P4EST_QUADRANT_LEN (q->level);
+      /* get the coordinate of the corner along the edge from zz's point of
+       * view */
+      if (e / 4 == 0) {
+        l = q->x + h * (c & 1);
+      }
+      else if (e / 4 == 1) {
+        l = q->y + h * ((c & 2) >> 1);
+      }
+      else {
+        l = q->z + h * ((c & 4) >> 2);
+      }
+      /* if the two edges are oppositely oriented, get the complement
+       * coordinate */
+      if ((owner_c > owner_c2) != (this_c > this_c2)) {
+        l = P4EST_ROOT_LEN - l;
+      }
+      /* we combine the knowledge about which edge we are looking for with the
+       * coordinate distance along the edge into the coordinates of the corner
+       */
+      h = P4EST_QUADRANT_LEN (0);
+      if (owner_e / 4 == 0) {
+        x = l;
+        y = h * (owner_e & 1);
+        z = h * ((owner_e & 2) >> 1);
+      }
+      else if (owner_e / 4 == 1) {
+        x = h * (owner_e & 1);
+        y = l;
+        z = h * ((owner_e & 2) >> 1);
+      }
+      else {
+        x = h * (owner_e & 1);
+        y = h * ((owner_e & 2) >> 1);
+        z = l;
+      }
+    }
+#endif
+    else {
+      int                 owner_c2, owner_f, c2;
+      p4est_topidx_t      nt;
+      int                 nf;
+
+      /* this uses some knowledge about how iterate orders the sides of a
+       * corner that is in the middle of a face */
+      P4EST_ASSERT (count == P4EST_HALF || count == P4EST_CHILDREN);
+      /* side[count - (count / P4EST_HALF)] is the same side of the face,
+       * opposite corner */
+      cside =
+        p4est_iter_cside_array_index (sides, count - (count / P4EST_HALF));
+      P4EST_ASSERT (cside->treeid == owner_tid);
+
+      owner_c2 = cside->corner;
+      /* the two coordinates determine the face */
+      owner_f = p4est_child_corner_faces[owner_c][owner_c2];
+      P4EST_ASSERT (owner_f >= 0);
+
+      /* figure out which tree is on the other side of the face */
+      nt = conn->tree_to_tree[P4EST_FACES * owner_tid + owner_f];
+      nf = conn->tree_to_face[P4EST_FACES * owner_tid + owner_f];
+
+      nf %= P4EST_FACES;
+
+      if ((nt == owner_tid && nf == owner_f) || (zz % 2) == 0) {
+        /* one-sided face: q must be on the same face: the corner is in the
+         * same coordinates */
+        h = P4EST_QUADRANT_LEN (q->level);
+        x = q->x + h * (c & 1);
+        y = q->y + h * ((c & 2) >> 1);
+#ifdef P4_TO_P8
+        z = q->z + h * ((c & 4) >> 2);
+#endif
+      }
+      else {
+        /* transform q across the face */
+        p4est_topidx_t      nnt;
+
+        P4EST_ASSERT (nt == tid);
+        nnt = p4est_quadrant_face_neighbor_extra (q, tid, nf, &tempq, NULL,
+                                                  conn);
+        P4EST_ASSERT (nnt == owner_tid);
+        c2 = p4est_quadrant_child_id (&tempq);
+        h = P4EST_QUADRANT_LEN (tempq.level);
+        x = tempq.x + h * (c2 & 1);
+        y = tempq.y + h * ((c2 & 2) >> 1);
+#ifdef P4_TO_P8
+        z = tempq.z + h * ((c2 & 4) >> 2);
+#endif
+      }
+    }
+    /* turn the coordinates of the corner into the coordinates of a smallest
+     * quad that touches the corner from the correct side */
+    h = P4EST_QUADRANT_LEN (P4EST_QMAXLEVEL);
+    ownq.x = x - h * (owner_c & 1);
+    ownq.y = y - h * ((owner_c & 2) >> 1);
+#ifdef P4_TO_P8
+    ownq.z = z - h * ((owner_c & 4) >> 2);
+#endif
+    ownq.level = P4EST_QMAXLEVEL;
+    /* find the owner */
+    owner_proc = p4est_comm_find_owner (info->p4est, owner_tid, &ownq, rank);
+    P4EST_ASSERT (owner_proc >= 0 && owner_proc != rank);
+  }
+  else {
+    if (owner_is_ghost) {
+      owner_proc = (int) sc_array_bsearch (&proc_offsets, &(owner_qid),
+                                           p4est_locidx_offset_compare);
+      P4EST_ASSERT (owner_proc >= 0 && owner_proc != rank);
+    }
+    else {
+      tree = p4est_tree_array_index (trees, owner_tid);
+      quadrants_offset = tree->quadrants_offset;
+      owner_qid += quadrants_offset;
+      owner_proc = rank;
+    }
+  }
+  /* create the new node */
+  for (j = 0; j < npc; j++) {
+    inode = (p4est_locidx_t *) sc_array_push (inodes);
+    inode[0] = owner_proc;
+    inode[1] = owner_qid;
+  }
+
+  /* figure out if this is a remote corner or one for which we can determing
+   * all touching and sharing procs */
+  has_local = 0;
+  for (zz = 0; zz < count; zz++) {
+    cside = p4est_iter_cside_array_index (sides, zz);
+    if (!cside->is_ghost) {
+      has_local = 1;
+    }
+  }
+  is_remote = !has_local;
+  if (is_remote) {
+    ip = (int *) sc_array_push (all_procs);
+    *ip = owner_proc;
+  }
+
+  for (zz = 0; zz < count; zz++) {
+    cside = p4est_iter_cside_array_index (sides, zz);
+    cside_get_fields (cside, &tid, &c, &is_ghost, &qid, &q);
+    if (q == NULL) {
+      P4EST_ASSERT (is_ghost);
+      continue;
+    }
+
+    tree = p4est_tree_array_index (trees, tid);
+    quadrants_offset = tree->quadrants_offset;
+    if (!is_ghost) {
+      proc = rank;
+      qid += quadrants_offset;
+      for (j = 0; j < npc; j++) {
+        nid = qid * nodes_per_elem + corner_nodes[c][j];
+        P4EST_ASSERT (local_elem_nodes[nid] == -1);
+        local_elem_nodes[nid] = num_inodes + j;
+      }
+    }
+    else if (!is_remote) {
+      P4EST_ASSERT (qid >= 0);
+      proc = (int) sc_array_bsearch (&proc_offsets, &qid,
+                                     p4est_locidx_offset_compare);
+      P4EST_ASSERT (proc >= 0 && proc != rank);
+      ip = (int *) sc_array_push (touching_procs);
+      *ip = proc;
+      ip = (int *) sc_array_push (all_procs);
+      *ip = proc;
+    }
+    else {
+      proc = -1;
+    }
+    if (p4est_quadrant_child_id (q) != c) {
+      /* there can be no remote quads / processes */
+      continue;
+    }
+    P4EST_ASSERT (qid >= 0);
+    if (is_ghost) {
+      P4EST_ASSERT ((size_t) qid < info->ghost_layer->ghosts.elem_count);
+    }
+    else {
+      P4EST_ASSERT (qid < info->p4est->local_num_quadrants);
+    }
+    dep = !is_ghost ? &(local_dep[qid]) : &(ghost_dep[qid]);
+#ifndef P4_TO_P8
+    limit = P4EST_DIM;
+#else
+    limit = 2 * P4EST_DIM;
+#endif
+    for (i = 0; i < limit; i++) {
+#ifndef P4_TO_P8
+      lp = &dep->face[i];
+#else
+      lp = (i < P4EST_DIM) ? &dep->face[i] : &dep->edge[i - 3];
+#endif
+      nqid = *lp;
+      if (nqid >= 0) {
+        has_local = 1;
+        /* remote local quad */
+        for (j = 0; j < npc; j++) {
+          nid = nqid * nodes_per_elem + corner_nodes[c][j];
+          P4EST_ASSERT (local_elem_nodes[nid] == -1);
+          local_elem_nodes[nid] = num_inodes + j;
+        }
+      }
+      else if (!is_remote) {
+        nproc = nqid;
+        if (nproc == -1) {
+          P4EST_ASSERT (is_ghost);
+          nproc = p4est_lnodes_missing_proc_corner (info, zz, i);
+          P4EST_ASSERT (nproc != rank);
+          P4EST_ASSERT (nproc >= -1);
+          P4EST_ASSERT (nproc < info->p4est->mpisize);
+          *lp = -((p4est_locidx_t) nproc + 3);
+        }
+        else {
+          nproc = -(nproc + 3);
+        }
+        P4EST_ASSERT (nproc >= -1 && nproc != rank);
+        if (nproc >= 0) {
+          P4EST_ASSERT (nproc != rank);
+          ip = (int *) sc_array_push (all_procs);
+          *ip = nproc;
+        }
+      }
+    }
+  }
+  P4EST_ASSERT (has_local);
+  sc_array_sort (touching_procs, sc_int_compare);
+  sc_array_uniq (touching_procs, sc_int_compare);
+  sc_array_sort (all_procs, sc_int_compare);
+  sc_array_uniq (all_procs, sc_int_compare);
+  count = all_procs->elem_count;
+  if (count) {
+    type = (int8_t) (P4EST_LN_C_OFFSET + owner_c);
+    p4est_lnodes_push_binfo (touching_procs, all_procs, send_buf_info,
+                             recv_buf_info, inode_sharers, owner_proc, rank,
+                             info->p4est->mpisize, is_remote, type,
+                             num_inodes);
+  }
+  else {
+    P4EST_ASSERT (owner_proc == rank);
+  }
+}
+
+#ifdef P4_TO_P8
+/** p8est_lnodes_missing_proc_edge: figure out processors that may share an
+ * edge node remotely
+ */
+static void
+p8est_lnodes_missing_proc_edge (p8est_iter_edge_info_t * info, int side,
+                                int b, int *mproc)
+{
+  sc_array_t         *sides = &(info->sides);
+  int                 i, nsides = (int) sides->elem_count;
+  p8est_iter_edge_side_t *thisside = p8est_iter_eside_array_index_int
+    (sides, side);
+  p8est_iter_edge_side_t *eside;
+  p4est_quadrant_t   *q, tempq, tempr;
+  int                 key, test;
+  int                 j;
+  int                 e = thisside->edge, f;
+  int                 edir = e / 4;
+  int                 missdir = 3 - edir - b;
+  int                 c, c2;
+
+  P4EST_ASSERT (edir != b);
+  P4EST_ASSERT (thisside->is_hanging);
+  P4EST_ASSERT (p8est_edge_faces[e][b < missdir ? 0 : 1] / 2 == b);
+  q = thisside->is.hanging.quad[0];
+  if (!q) {
+    q = thisside->is.hanging.quad[1];
+    P4EST_ASSERT (q);
+  }
+  key = thisside->faces[b < missdir ? 0 : 1];
+  c = p8est_edge_corners[e][0];
+  f = p8est_corner_faces[c][b];
+  c = p8est_corner_face_corners[c][f];
+  c = p8est_face_corners[f][c ^ 3];
+  c2 = p8est_edge_corners[e][1];
+  c2 = p8est_corner_face_corners[c2][f];
+  c2 = p8est_face_corners[f][c2 ^ 3];
+  p4est_quadrant_sibling (q, &tempq, c);
+  p4est_quadrant_sibling (q, &tempr, c2);
+  for (i = 0; i < nsides; i++) {
+    if (i == side) {
+      continue;
+    }
+    eside = p8est_iter_eside_array_index_int (sides, i);
+    for (j = 0; j < 2; j++) {
+      test = eside->faces[j];
+      if (test == key) {
+        if (!eside->is_hanging && eside->is.full.quad != NULL) {
+          mproc[0] =
+            p4est_comm_find_owner (info->p4est, thisside->treeid, &tempq,
+                                   info->p4est->mpirank);
+          P4EST_ASSERT (mproc[0] >= 0);
+          mproc[1] =
+            p4est_comm_find_owner (info->p4est, thisside->treeid, &tempr,
+                                   mproc[0]);
+          P4EST_ASSERT (mproc[1] >= 0);
+          return;
+        }
+        else {
+          mproc[0] = -1;
+          mproc[1] = -1;
+          return;
+        }
+      }
+    }
+  }
+  mproc[0] = -1;
+  mproc[1] = -1;
+}
+
+/* p8est_lnodes_edge_callback:
+ *
+ * Create new independent nodes on an edge.
+ * Set all touching element nodes to point to the newly created independent
+ * nodes.
+ * Compute all processes that share the nodes.
+ * If the nodes are locally owned, add info describing the nodes to the send
+ * buffer of all processes that share the nodes.
+ * If the nodes are not locally owned, add info describing the nodes to the
+ * receive buffer of the owner.
+ */
+static void
+p8est_lnodes_edge_callback (p8est_iter_edge_info_t * info, void *Data)
+{
+  int                 i, j, k, xdir[2];
+  sc_array_t         *sides = &(info->sides);
+  size_t              zz, count = sides->elem_count;
+  p4est_lnodes_data_t *data = (p4est_lnodes_data_t *) Data;
+  p8est_iter_edge_side_t *eside, *owner_eside;
+  sc_array_t         *inodes = data->inodes;
+  p4est_locidx_t     *inode;
+  sc_array_t         *inode_sharers = data->inode_sharers;
+  p4est_lnodes_dep_t *local_dep = data->local_dep;
+  p4est_lnodes_dep_t *ghost_dep = data->ghost_dep;
+  p4est_lnodes_dep_t *dep;
+  p4est_locidx_t     *local_elem_nodes = data->local_elem_nodes;
+  sc_array_t         *send_buf_info = data->send_buf_info;
+  sc_array_t         *recv_buf_info = data->recv_buf_info;
+  sc_array_t         *touching_procs = data->touching_procs;
+  sc_array_t         *all_procs = data->all_procs;
+  int                *ip;
+  p4est_topidx_t      tid, owner_tid;
+  sc_array_t         *trees = info->p4est->trees;
+  p4est_tree_t       *tree;
+  p4est_locidx_t      quadrants_offset;
+  p4est_locidx_t     *qids;
+  p4est_locidx_t      qid, owner_qid, nqid;
+  p4est_locidx_t      num_inodes = (p4est_locidx_t) inodes->elem_count;
+  int8_t             *is_ghost, owner_is_ghost;
+  int                 e, edir, owner_e, owner_c, o;
+  p4est_locidx_t      nid;
+  int                 owner_proc, nproc;
+  int                 rank = info->p4est->mpirank;
+  p4est_quadrant_t   *owner_q = NULL;
+  p4est_quadrant_t   *q;
+  p4est_quadrant_t  **quad;
+  p4est_quadrant_t    tempq, tempr, ownq;
+  int                 nodes_per_edge = data->nodes_per_edge;
+  int                 nodes_per_elem = data->nodes_per_elem;
+  int               **edge_nodes = data->edge_nodes;
+  int                 is_hanging;
+  int                 limit;
+  int                 stride;
+  p4est_locidx_t      start_node;
+  int8_t              type;
+  int                 is_remote, has_local;
+  p4est_connectivity_t *conn = info->p4est->connectivity;
+  int                 mproc[2][2] = { {-1, -1}, {-1, -1} };
+
+  sc_array_truncate (touching_procs);
+  sc_array_truncate (all_procs);
+  has_local = p8est_lnodes_edge_simple_callback (info, data);
+
+  owner_eside = p8est_iter_eside_array_index (sides, 0);
+  owner_e = owner_eside->edge;
+  owner_tid = owner_eside->treeid;
+  if (owner_eside->is_hanging) {
+    owner_qid = owner_eside->is.hanging.quadid[0];
+    owner_is_ghost = owner_eside->is.hanging.is_ghost[0];
+    owner_q = owner_eside->is.hanging.quad[0];
+  }
+  else {
+    owner_qid = owner_eside->is.full.quadid;
+    owner_is_ghost = owner_eside->is.full.is_ghost;
+    owner_q = owner_eside->is.full.quad;
+  }
+  P4EST_ASSERT (!owner_eside->orientation);
+  owner_c = p8est_edge_corners[owner_e][0];
+  if (owner_q == NULL) {
+    int                 c;
+    p4est_qcoord_t      x, y, z, h, l;
+
+    P4EST_ASSERT (count > 1);
+    eside = NULL;
+    for (zz = 1; zz < count; zz++) {
+      eside = p8est_iter_eside_array_index (sides, zz);
+      if ((!eside->is_hanging) && eside->is.full.quad) {
+        break;
+      }
+    }
+    P4EST_ASSERT (zz < count);
+    e = eside->edge;
+    tid = eside->treeid;
+    o = eside->orientation;
+    c = p8est_edge_corners[e][o];
+    q = eside->is.full.quad;
+    P4EST_ASSERT (q->level < P4EST_QMAXLEVEL);
+    p4est_quadrant_corner_descendant (q, &tempr, c, q->level + 1);
+    q = &tempr;
+    P4EST_ASSERT (c == p4est_quadrant_child_id (q));
+    p8est_quadrant_edge_neighbor (q, e, &tempq);
+    /* get the coordinates of the edge */
+    if (p4est_quadrant_is_inside_root (&tempq)) {
+      h = P4EST_QUADRANT_LEN (q->level);
+      x = q->x + h * (c & 1);
+      y = q->y + h * ((c & 2) >> 1);
+      z = q->z + h * ((c & 4) >> 2);
+    }
+    else if (p8est_quadrant_is_outside_edge (&tempq)) {
+      h = P4EST_QUADRANT_LEN (q->level);
+      if (e / 4 == 0) {
+        l = q->x + h * (c & 1);
+      }
+      else if (e / 4 == 1) {
+        l = q->y + h * ((c & 2) >> 1);
+      }
+      else {
+        l = q->z + h * ((c & 4) >> 2);
+      }
+      if (o) {
+        l = P4EST_ROOT_LEN - l;
+      }
+      h = P4EST_QUADRANT_LEN (0);
+      if (owner_e / 4 == 0) {
+        x = l;
+        y = h * (owner_e & 1);
+        z = h * ((owner_e & 2) >> 1);
+      }
+      else if (owner_e / 4 == 1) {
+        x = h * (owner_e & 1);
+        y = l;
+        z = h * ((owner_e & 2) >> 1);
+      }
+      else {
+        x = h * (owner_e & 1);
+        y = h * ((owner_e & 2) >> 1);
+        z = l;
+      }
+    }
+    else {
+      /* outside face */
+      int                 owner_f, c1, c2, nf;
+      p4est_topidx_t      nt;
+      /* this uses some knowledge about how iterate orders the sides of a
+       * corner that is in the middle of a face */
+      P4EST_ASSERT (count == 2 || count == 4);
+      eside = p8est_iter_eside_array_index (sides, count / 2);
+      P4EST_ASSERT (eside->treeid == owner_tid);
+      P4EST_ASSERT (eside->edge != owner_e);
+
+      c1 = p8est_edge_corners[owner_e][0];
+      c2 = p8est_edge_corners[eside->edge][1];
+      owner_f = p4est_child_corner_faces[c1][c2];
+      P4EST_ASSERT (owner_f >= 0);
+
+      nt = conn->tree_to_tree[P4EST_FACES * owner_tid + owner_f];
+      nf = conn->tree_to_face[P4EST_FACES * owner_tid + owner_f];
+
+      /* o2 = nf / P4EST_FACES; */
+      nf %= P4EST_FACES;
+
+      if ((nt == owner_tid && nf == owner_f) || (zz % 2) == 0) {
+        /* q must be on the same side: the corner is in the same coordinates
+         */
+        P4EST_ASSERT (!o);
+        h = P4EST_QUADRANT_LEN (q->level);
+        x = q->x + h * (c & 1);
+        y = q->y + h * ((c & 2) >> 1);
+        z = q->z + h * ((c & 4) >> 2);
+      }
+      else {
+        int                 c2;
+        p4est_topidx_t      nnt;
+
+        P4EST_ASSERT (nt == tid);
+        P4EST_ASSERT (count == 4);
+        nnt =
+          p4est_quadrant_face_neighbor_extra (q, tid, nf, &tempq, NULL, conn);
+        P4EST_ASSERT (nnt == owner_tid);
+        c2 = p4est_quadrant_child_id (&tempq);
+        P4EST_ASSERT (p4est_corner_face_corners[c2][owner_f] >= 0);
+        h = P4EST_QUADRANT_LEN (tempq.level);
+        x = tempq.x + h * (c2 & 1);
+        y = tempq.y + h * ((c2 & 2) >> 1);
+        z = tempq.z + h * ((c2 & 4) >> 2);
+      }
+    }
+    h = P4EST_QUADRANT_LEN (P4EST_QMAXLEVEL);
+    ownq.x = x - h * (owner_c & 1);
+    ownq.y = y - h * ((owner_c & 2) >> 1);
+#ifdef P4_TO_P8
+    ownq.z = z - h * ((owner_c & 4) >> 2);
+#endif
+    ownq.level = P4EST_QMAXLEVEL;
+    owner_proc = p4est_comm_find_owner (info->p4est, owner_tid, &ownq, rank);
+  }
+  else {
+    if (owner_is_ghost) {
+      owner_proc = *((int *) sc_array_index (touching_procs, 0));
+      P4EST_ASSERT (owner_proc >= 0 && owner_proc != rank);
+    }
+    else {
+      owner_proc = rank;
+      tree = p4est_tree_array_index (trees, owner_tid);
+      quadrants_offset = tree->quadrants_offset;
+      owner_qid += quadrants_offset;
+    }
+  }
+  if (has_local) {
+    sc_array_sort (touching_procs, sc_int_compare);
+    sc_array_uniq (touching_procs, sc_int_compare);
+  }
+  /* create nodes */
+  for (i = 0; i < nodes_per_edge; i++) {
+    inode = (p4est_locidx_t *) sc_array_push (inodes);
+    P4EST_ASSERT (inodes->elem_count <= (size_t)
+                  (nodes_per_elem * info->p4est->local_num_quadrants));
+    inode[0] = owner_proc;
+    inode[1] = owner_qid;
+  }
+  /* point element nodes at created nodes; find all sharing procs */
+  is_remote = !has_local;
+  if (!is_remote) {
+    sc_array_copy (all_procs, touching_procs);
+  }
+  else {
+    ip = (int *) sc_array_push (all_procs);
+    *ip = owner_proc;
+  }
+  for (zz = 0; zz < count; zz++) {
+    eside = p8est_iter_eside_array_index (sides, zz);
+    limit = eside_get_fields (eside, &is_hanging, &tid, &e, &o, &is_ghost,
+                              &qids, &quad);
+    tree = p4est_tree_array_index (trees, tid);
+    quadrants_offset = tree->quadrants_offset;
+    if (!is_hanging && quad[0] == NULL) {
+      continue;
+    }
+    mproc[0][0] = -2;
+    mproc[0][1] = -2;
+    mproc[1][0] = -2;
+    mproc[1][1] = -2;
+    for (i = 0; i < limit; i++) {
+      qid = qids[i];
+      if (qid < 0) {
+        continue;
+      }
+      stride = (o ? -1 : 1);
+      if (!is_ghost[i]) {
+        qid += quadrants_offset;
+        P4EST_ASSERT (qid < info->p4est->local_num_quadrants);
+        start_node = num_inodes + (o ? nodes_per_edge - 1 : 0);
+        for (k = 0; k < nodes_per_edge; k++, start_node += stride) {
+          nid = qid * nodes_per_elem + edge_nodes[e][k];
+          P4EST_ASSERT (local_elem_nodes[nid] == -1);
+          local_elem_nodes[nid] = start_node;
+        }
+      }
+      if (!is_hanging) {
+        continue;
+      }
+      if (is_remote && qids[i ^ 1] < 0) {
+        continue;
+      }
+      /* get quads that may be dependent because of hanging faces */
+      dep = !is_ghost[i] ? &local_dep[qid] : &ghost_dep[qid];
+      edir = e / 4;
+      for (j = 0; j < 2; j++) {
+        xdir[0] = (edir + j + 1) % 3;
+        xdir[1] = (edir + 2 - j) % 3;
+        if (dep->face[xdir[1]] == -1) {
+          P4EST_ASSERT (is_ghost[i]);
+          if (!is_ghost[i ^ 1]) {
+            P4EST_ASSERT (local_dep[qids[i ^ 1] + quadrants_offset].face
+                          [xdir[1]] == -2);
+            dep->face[xdir[1]] = -2;
+          }
+          else if (mproc[j][i] == -2) {
+            p8est_lnodes_missing_proc_edge (info, zz, xdir[1],
+                                            &(mproc[j][0]));
+            P4EST_ASSERT (mproc[j][0] != -2 && mproc[j][1] != -2);
+            P4EST_ASSERT (mproc[j][0] != rank && mproc[j][1] != rank);
+            for (k = 0; k < 2; k++) {
+              if (mproc[j][k] >= 0) {
+                ip = (int *) sc_array_push (all_procs);
+                *ip = mproc[j][k];
+              }
+            }
+            dep->face[xdir[1]] = -((p4est_locidx_t) mproc[j][i] + 3);
+          }
+          else {
+            dep->face[xdir[1]] = -((p4est_locidx_t) mproc[j][i] + 3);
+          }
+        }
+        if (dep->face[xdir[1]] == -2) {
+          continue;
+        }
+        nqid = dep->edge[xdir[0]];
+        if (nqid >= 0) {
+          has_local = 1;
+          start_node = num_inodes + (o ? nodes_per_edge - 1 : 0);
+          for (k = 0; k < nodes_per_edge; k++, start_node += stride) {
+            nid = nqid * nodes_per_elem + edge_nodes[e][k];
+            P4EST_ASSERT (local_elem_nodes[nid] == -1);
+            local_elem_nodes[nid] = start_node;
+          }
+        }
+        else if (!is_remote) {
+          nproc = nqid;
+          if (nproc == -1) {
+            nproc = mproc[j][i ^ 1];
+            dep->edge[xdir[0]] = -((p4est_locidx_t) nproc + 3);
+          }
+          else {
+            nproc = -(nproc + 3);
+          }
+          P4EST_ASSERT (nproc >= -1);
+          if (nproc >= 0 && nproc != rank) {
+            ip = (int *) sc_array_push (all_procs);
+            *ip = nproc;
+          }
+        }
+      }
+    }
+  }
+  P4EST_ASSERT (has_local);
+  sc_array_sort (all_procs, sc_int_compare);
+  sc_array_uniq (all_procs, sc_int_compare);
+
+  count = all_procs->elem_count;
+  if (count) {
+    type = (int8_t) (P8EST_LN_E_OFFSET + owner_e);
+    p4est_lnodes_push_binfo (touching_procs, all_procs, send_buf_info,
+                             recv_buf_info, inode_sharers, owner_proc, rank,
+                             info->p4est->mpisize, is_remote,
+                             type, num_inodes);
+  }
+  else {
+    P4EST_ASSERT (owner_proc == rank);
+  }
+}
+
+/** p8est_lnodes_face_node_transform:
+ *
+ * Compute the transformation from an independent node's position on a face
+ * to the position on the face of the touching element.
+ */
+static inline void
+p8est_lnodes_face_node_transform (int orig_f, int f, int8_t orientation,
+                                  int8_t * flipj, int8_t * flipk,
+                                  int8_t * swapjk)
+{
+  int                 ref = p8est_face_permutation_refs[f][orig_f];
+  int                 set = p8est_face_permutation_sets[ref][orientation];
+  int                 c0 = p8est_face_permutations[set][0];
+  int                 c1 = p8est_face_permutations[set][1];
+  int                 c2 = p8est_face_permutations[set][2];
+  *flipj = (c1 < c0);
+  *flipk = (c2 < c0);
+  *swapjk = ((c0 ^ c2) == 1);
+}
+#endif
+
+/* p4est_lnodes_face_callback:
+ *
+ * Create new independent nodes on a face.
+ * Set all touching element nodes to point to the newly created independent
+ * nodes.
+ * Compute all processes that share the nodes.
+ * If the nodes are locally owned, add info describing the nodes to the send
+ * buffer of all processes that share the nodes.
+ * If the nodes are not locally owned, add info describing the nodes to the
+ * receive buffer of the owner.
+ */
+static void
+p4est_lnodes_face_callback (p4est_iter_face_info_t * info, void *Data)
+{
+  sc_array_t         *sides = &(info->sides);
+  size_t              zz, count = sides->elem_count;
+  p4est_lnodes_data_t *data = (p4est_lnodes_data_t *) Data;
+  p4est_iter_face_side_t *fside;
+  sc_array_t         *inodes = data->inodes;
+  p4est_locidx_t     *inode;
+  sc_array_t         *inode_sharers = data->inode_sharers;
+  p4est_locidx_t     *local_elem_nodes = data->local_elem_nodes;
+  sc_array_t         *send_buf_info = data->send_buf_info;
+  sc_array_t         *recv_buf_info = data->recv_buf_info;
+  sc_array_t         *touching_procs = data->touching_procs;
+  p4est_topidx_t      tid;
+  sc_array_t         *trees = info->p4est->trees;
+  p4est_tree_t       *tree;
+  p4est_locidx_t      quadrants_offset;
+  p4est_locidx_t     *qids;
+  p4est_locidx_t      qid, owner_qid;
+  p4est_locidx_t      num_inodes = (p4est_locidx_t) inodes->elem_count;
+  int8_t             *is_ghost, owner_is_ghost;
+  int                 f, owner_f;
+  p4est_locidx_t      nid;
+  int                 owner_proc;
+  int                 rank = info->p4est->mpirank;
+  p4est_quadrant_t  **q;
+  /* p4est_quadrant_t   *owner_q; */
+  int                 nodes_per_face = data->nodes_per_face;
+  int                 nodes_per_elem = data->nodes_per_elem;
+  int               **face_nodes = data->face_nodes;
+  int                 is_hanging;
+  int                 i, j, limit;
+#ifndef P4_TO_P8
+  int                 stride;
+#else
+  int                 nodes_per_edge = SC_MAX (1, data->nodes_per_edge);
+  int8_t              flipj, flipk, swapjk;
+  int                 k, l, jind, kind, lind;
+#endif
+  p4est_locidx_t      start_node;
+  int8_t              type;
+
+  sc_array_truncate (touching_procs);
+  p4est_lnodes_face_simple_callback (info, data);
+
+  /* the first touching quad is the owner */
+  fside = p4est_iter_fside_array_index (sides, 0);
+  if (fside->is_hanging) {
+    /* owner_q = fside->is.hanging.quad[0]; */
+    owner_is_ghost = fside->is.hanging.is_ghost[0];
+    owner_qid = fside->is.hanging.quadid[0];
+    owner_f = fside->face;
+  }
+  else {
+    /* owner_q = fside->is.full.quad; */
+    owner_is_ghost = fside->is.full.is_ghost;
+    owner_qid = fside->is.full.quadid;
+    owner_f = fside->face;
+  }
+  if (!(owner_is_ghost)) {
+    owner_proc = rank;
+    tid = fside->treeid;
+    tree = p4est_tree_array_index (trees, tid);
+    quadrants_offset = tree->quadrants_offset;
+    owner_qid += quadrants_offset;
+  }
+  else {
+    owner_proc = *((int *) sc_array_index (touching_procs, 0));
+    P4EST_ASSERT (owner_proc >= 0 && owner_proc != rank);
+  }
+  sc_array_sort (touching_procs, sc_int_compare);
+  sc_array_uniq (touching_procs, sc_int_compare);
+  /* create the nodes */
+  for (i = 0; i < nodes_per_face; i++) {
+    inode = (p4est_locidx_t *) sc_array_push (inodes);
+    P4EST_ASSERT (inodes->elem_count <= (size_t)
+                  (nodes_per_elem * info->p4est->local_num_quadrants));
+    inode[0] = owner_proc;
+    inode[1] = owner_qid;
+  }
+
+  /* point element nodes to created nodes */
+  for (zz = 0; zz < count; zz++) {
+    fside = p4est_iter_fside_array_index (sides, zz);
+    limit = fside_get_fields (fside, &is_hanging, &tid, &f, &is_ghost, &qids,
+                              &q);
+    tree = p4est_tree_array_index (trees, tid);
+    quadrants_offset = tree->quadrants_offset;
+
+    for (i = 0; i < limit; i++) {
+      qid = qids[i];
+      if (!is_ghost[i]) {
+        qid += quadrants_offset;
+#ifndef P4_TO_P8
+        start_node = num_inodes + (!zz ? 0 :
+                                   !info->orientation ? 0 :
+                                   nodes_per_face - 1);
+        stride = !zz ? 1 : !info->orientation ? 1 : -1;
+        for (j = 0; j < nodes_per_face; j++, start_node += stride) {
+          nid = qid * nodes_per_elem + face_nodes[f][j];
+          P4EST_ASSERT (local_elem_nodes[nid] == -1);
+          local_elem_nodes[nid] = start_node;
+        }
+#else
+        if (!zz) {
+          flipj = 0;
+          flipk = 0;
+          swapjk = 0;
+        }
+        else {
+          p8est_lnodes_face_node_transform (owner_f, f, info->orientation,
+                                            &flipj, &flipk, &swapjk);
+        }
+        start_node = num_inodes;
+        for (l = 0, k = 0; k < nodes_per_edge; k++) {
+          for (j = 0; j < nodes_per_edge; j++, l++) {
+            nid = qid * nodes_per_elem + face_nodes[f][l];
+            jind = flipj ? (nodes_per_edge - 1 - j) : j;
+            kind = flipk ? (nodes_per_edge - 1 - k) : k;
+            lind = swapjk ? (nodes_per_edge * jind + kind) :
+              (nodes_per_edge * kind + jind);
+            P4EST_ASSERT (local_elem_nodes[nid] == -1);
+            local_elem_nodes[nid] = start_node + lind;
+          }
+        }
+#endif
+      }
+    }
+  }
+
+  count = touching_procs->elem_count;
+  if (count) {
+    type = (int8_t) owner_f;
+    p4est_lnodes_push_binfo (NULL, touching_procs, send_buf_info,
+                             recv_buf_info, inode_sharers, owner_proc, rank,
+                             info->p4est->mpisize, 0, type, num_inodes);
+  }
+}
+
+/* p4est_lnodes_volume_callback:
+ *
+ * Create independent nodes and set volume nodes to point to them.
+ */
+static void
+p4est_lnodes_volume_callback (p4est_iter_volume_info_t * info, void *Data)
+{
+  p4est_lnodes_data_t *data = (p4est_lnodes_data_t *) Data;
+  p4est_tree_t       *tree = p4est_tree_array_index (info->p4est->trees,
+                                                     info->treeid);
+  p4est_locidx_t      qid = info->quadid + tree->quadrants_offset;
+  p4est_locidx_t     *elem_nodes = data->local_elem_nodes;
+  sc_array_t         *inodes = data->inodes;
+  p4est_locidx_t      num_inodes = (p4est_locidx_t) inodes->elem_count;
+  p4est_locidx_t      nid;
+  p4est_locidx_t     *inode;
+  int                 nodes_per_volume = data->nodes_per_volume;
+  int                *volume_nodes = data->volume_nodes;
+  int                 nodes_per_elem = data->nodes_per_elem;
+  int                 i;
+  int                 rank = info->p4est->mpirank;
+
+  for (i = 0; i < nodes_per_volume; i++) {
+    nid = qid * nodes_per_elem + volume_nodes[i];
+    P4EST_ASSERT (elem_nodes[nid] == -1);
+    elem_nodes[nid] = num_inodes + (p4est_locidx_t) i;
+    inode = (p4est_locidx_t *) sc_array_push (inodes);
+    P4EST_ASSERT (inodes->elem_count <= (size_t)
+                  (nodes_per_elem * info->p4est->local_num_quadrants));
+    inode[0] = rank;
+    inode[1] = qid;
+  }
+}
+
+static void
+p4est_lnodes_init_data (p4est_lnodes_data_t * data, int p, p4est_t * p4est,
+                        p4est_ghost_t * ghost_layer, p4est_lnodes_t * lnodes)
+{
+  int                 i, j, n;
+  int                 npv;
+  int                 vcount;
+  int                 npf, npc;
+  int                 fcount[P4EST_FACES];
+  int                 ccount[P4EST_CHILDREN];
+  int                 f;
+  int                 bcount;
+  int                 c;
+#ifdef P4_TO_P8
+  int                 e;
+  int                 eshift;
+  int                 k;
+  int                 npe;
+  int                 ecount[P8EST_EDGES];
+#endif
+  p4est_locidx_t      nlq = p4est->local_num_quadrants;
+  p4est_locidx_t      ngq = (p4est_locidx_t) ghost_layer->ghosts.elem_count;
+  p4est_locidx_t      nldep = nlq;
+  p4est_locidx_t      ngdep = ngq;
+  int                 mpisize = p4est->mpisize;
+
+  if (p == -1) {
+    data->nodes_per_elem = P4EST_FACES;
+    npv = data->nodes_per_volume = 0;
+    npf = data->nodes_per_face = 1;
+#ifdef P4_TO_P8
+    npe = data->nodes_per_edge = 0;
+#endif
+    npc = data->nodes_per_corner = 0;
+  }
+#ifdef P4_TO_P8
+  else if (p == -2) {
+    data->nodes_per_elem = P4EST_FACES + P8EST_EDGES;
+    npv = data->nodes_per_volume = 0;
+    npf = data->nodes_per_face = 1;
+    npe = data->nodes_per_edge = 1;
+    npc = data->nodes_per_corner = 0;
+  }
+#endif
+  else if (p == -P4EST_DIM) {
+    data->nodes_per_elem = P4EST_FACES +
+#ifdef P4_TO_P8
+      P8EST_EDGES +
+#endif
+      P4EST_CHILDREN;
+
+    npv = data->nodes_per_volume = 0;
+    npf = data->nodes_per_face = 1;
+#ifdef P4_TO_P8
+    npe = data->nodes_per_edge = 1;
+#endif
+    npc = data->nodes_per_corner = 1;
+  }
+  else {
+#ifndef P4_TO_P8
+    data->nodes_per_elem = (p + 1) * (p + 1);
+    npv = data->nodes_per_volume = (p - 1) * (p - 1);
+    npf = data->nodes_per_face = p - 1;
+#else
+    data->nodes_per_elem = (p + 1) * (p + 1) * (p + 1);
+    npv = data->nodes_per_volume = (p - 1) * (p - 1) * (p - 1);
+    npf = data->nodes_per_face = (p - 1) * (p - 1);
+    npe = data->nodes_per_edge = (p - 1);
+#endif
+    npc = data->nodes_per_corner = 1;
+  }
+#ifndef P4_TO_P8
+  fcount[0] = fcount[1] = fcount[2] = fcount[3] = 0;
+  ccount[0] = ccount[1] = ccount[2] = ccount[3] = 0;
+#else
+  fcount[0] = fcount[1] = fcount[2] = fcount[3] = fcount[4] = fcount[5] = 0;
+  ccount[0] = ccount[1] = ccount[2] = ccount[3] = 0;
+  ccount[4] = ccount[5] = ccount[6] = ccount[7] = 0;
+  ecount[0] = ecount[1] = ecount[2] = ecount[3] = ecount[4] = ecount[5] = 0;
+  ecount[6] = ecount[7] = ecount[8] = ecount[9] = ecount[10] = ecount[11] = 0;
+#endif
+  vcount = 0;
+
+  data->volume_nodes = P4EST_ALLOC (int, npv);
+  for (i = 0; i < P4EST_FACES; i++) {
+    data->face_nodes[i] = P4EST_ALLOC (int, npf);
+  }
+#ifdef P4_TO_P8
+  for (i = 0; i < P8EST_EDGES; i++) {
+    data->edge_nodes[i] = P4EST_ALLOC (int, npe);
+  }
+#endif
+  for (i = 0; i < P4EST_CHILDREN; i++) {
+    data->corner_nodes[i] = P4EST_ALLOC (int, npc);
+  }
+
+  if (p > 0) {
+    /* figure out which nodes live on which parts of the quadrants */
+    n = 0;
+#ifdef P4_TO_P8
+    for (k = 0; k < p + 1; k++) {
+#endif
+      for (j = 0; j < p + 1; j++) {
+        for (i = 0; i < p + 1; i++, n++) {
+          bcount = f = c = 0;
+#ifdef P4_TO_P8
+          e = 0;
+          eshift = -1;
+          switch (k == 0 ? 0 : k == p ? 1 : 2) {
+          case 0:
+            f = 4;
+            bcount++;
+            break;
+          case 1:
+            f = 5;
+            c |= 4;
+            e++;
+            bcount++;
+            break;
+          default:
+            eshift = 8;
+            break;
+          }
+#endif
+          switch (j == 0 ? 0 : j == p ? 1 : 2) {
+          case 0:
+            f = 2;
+#ifdef P4_TO_P8
+            e <<= 1;
+#endif
+            bcount++;
+            break;
+          case 1:
+            f = 3;
+            c |= 2;
+#ifdef P4_TO_P8
+            e <<= 1;
+            e++;
+#endif
+            bcount++;
+            break;
+          default:
+#ifdef P4_TO_P8
+            eshift = 4;
+#endif
+            break;
+          }
+          switch (i == 0 ? 0 : i == p ? 1 : 2) {
+          case 0:
+            bcount++;
+#ifdef P4_TO_P8
+            e <<= 1;
+#endif
+            break;
+          case 1:
+            f = 1;
+            c |= 1;
+#ifdef P4_TO_P8
+            e <<= 1;
+            e++;
+#endif
+            bcount++;
+            break;
+          default:
+#ifdef P4_TO_P8
+            eshift = 0;
+#endif
+            break;
+          }
+          switch (bcount) {
+          case 0:
+            data->volume_nodes[vcount++] = n;
+            break;
+          case 1:
+            data->face_nodes[f][fcount[f]++] = n;
+            break;
+#ifdef P4_TO_P8
+          case 2:
+            P4EST_ASSERT (eshift >= 0);
+            e += eshift;
+            data->edge_nodes[e][ecount[e]++] = n;
+            break;
+#endif
+          default:
+            data->corner_nodes[c][ccount[c]++] = n;
+            break;
+          }
+        }
+      }
+#ifdef P4_TO_P8
+    }
+#endif
+  }
+  else {
+    int                 offset = 0;
+
+    for (i = 0; i < npv; i++) {
+      data->volume_nodes[vcount++] = offset++;
+    }
+    for (f = 0; f < P4EST_FACES; f++) {
+      for (i = 0; i < npf; i++) {
+        data->face_nodes[f][fcount[f]++] = offset++;
+      }
+    }
+#ifdef P4_TO_P8
+    for (e = 0; e < P8EST_EDGES; e++) {
+      for (i = 0; i < npe; i++) {
+        data->edge_nodes[e][ecount[e]++] = offset++;
+      }
+    }
+#endif
+    for (c = 0; c < P4EST_CHILDREN; c++) {
+      for (i = 0; i < npc; i++) {
+        data->corner_nodes[c][ccount[c]++] = offset++;
+      }
+    }
+  }
+
+  data->local_dep = P4EST_ALLOC (p4est_lnodes_dep_t, nldep);
+  memset (data->local_dep, -1, nldep * sizeof (p4est_lnodes_dep_t));
+  data->ghost_dep = P4EST_ALLOC (p4est_lnodes_dep_t, ngdep);
+  memset (data->ghost_dep, -1, ngdep * sizeof (p4est_lnodes_dep_t));
+
+  data->local_elem_nodes = lnodes->element_nodes;
+
+  data->inodes = sc_array_new (2 * sizeof (p4est_locidx_t));
+  data->inode_sharers = sc_array_new (sizeof (int));
+  data->send_buf_info = P4EST_ALLOC (sc_array_t, mpisize);
+  data->recv_buf_info = P4EST_ALLOC (sc_array_t, mpisize);
+  for (i = 0; i < mpisize; i++) {
+    sc_array_init (&(data->send_buf_info[i]),
+                   sizeof (p4est_lnodes_buf_info_t));
+    sc_array_init (&(data->recv_buf_info[i]),
+                   sizeof (p4est_lnodes_buf_info_t));
+  }
+  data->face_codes = lnodes->face_code;
+  data->poff = P4EST_ALLOC_ZERO (p4est_locidx_t, mpisize + 1);
+  data->touching_procs = sc_array_new (sizeof (int));
+  data->all_procs = sc_array_new (sizeof (int));
+}
+
+static void
+p4est_lnodes_reset_data (p4est_lnodes_data_t * data, p4est_t * p4est)
+{
+  int                 mpisize = p4est->mpisize;
+  int                 i;
+
+  sc_array_destroy (data->touching_procs);
+  sc_array_destroy (data->all_procs);
+  P4EST_FREE (data->poff);
+  P4EST_FREE (data->volume_nodes);
+  for (i = 0; i < P4EST_FACES; i++) {
+    P4EST_FREE (data->face_nodes[i]);
+  }
+#ifdef P4_TO_P8
+  for (i = 0; i < P8EST_EDGES; i++) {
+    P4EST_FREE (data->edge_nodes[i]);
+  }
+#endif
+  for (i = 0; i < P4EST_CHILDREN; i++) {
+    P4EST_FREE (data->corner_nodes[i]);
+  }
+
+  sc_array_destroy (data->inodes);
+  sc_array_destroy (data->inode_sharers);
+  for (i = 0; i < mpisize; i++) {
+    sc_array_reset (&(data->send_buf_info[i]));
+    sc_array_reset (&(data->recv_buf_info[i]));
+  }
+  P4EST_FREE (data->send_buf_info);
+  P4EST_FREE (data->recv_buf_info);
+  P4EST_FREE (data->local_dep);
+  P4EST_FREE (data->ghost_dep);
+  /* do not free face_codes: controlled by lnodes_t */
+}
+
+/* p4est_lnodes_count_send:
+ *
+ * Coming out of the main iteration that finds the independent nodes, but before
+ * hanging faces and hanging edges have been fixed, we assign numbers to
+ * independent nodes based on greedy numbering: looping through the quadrants,
+ * a quadrant numbers of node if it touches it (touches the
+ * volume/face/edge/corner containing it) and if it hasn't received a number
+ * yet.  This is consistent with the node ownership scheme.
+ *
+ * At this point each process knows where and what it needs to send, so the
+ * sends are initiated.
+ */
+static void
+p4est_lnodes_count_send (p4est_lnodes_data_t * data, p4est_t * p4est,
+                         p4est_lnodes_t * lnodes)
+{
+  p4est_locidx_t      nlq = p4est->local_num_quadrants;
+  p4est_locidx_t      nlen, nln;
+  p4est_locidx_t      li, *lp;
+  p4est_locidx_t      inidx;
+  sc_array_t         *inodes = data->inodes;
+  p4est_locidx_t     *inode;
+  p4est_topidx_t     *local_en = data->local_elem_nodes;
+  int                 i, j;
+  int                 rank = p4est->mpirank;
+  int                 mpisize = p4est->mpisize;
+  int                 npe = data->nodes_per_elem;
+  p4est_locidx_t      count = 0;
+  sc_array_t         *send_buf_info = data->send_buf_info;
+  sc_array_t         *send_info;
+  sc_array_t         *send;
+  size_t              zz;
+  size_t              zindex;
+  p4est_lnodes_buf_info_t *binfo;
+  int8_t              type;
+  int                 limit;
+  int                 nodes_per_face = data->nodes_per_face;
+#ifdef P4_TO_P8
+  int                 nodes_per_edge = data->nodes_per_edge;
+#endif
+  int                 nodes_per_corner = data->nodes_per_corner;
+  int                 share_count;
+  int                 share_proc;
+  sc_array_t         *inode_sharers = data->inode_sharers;
+  size_t              send_count;
+  sc_MPI_Request     *send_request;
+  int                 num_send_procs;
+  size_t              total_sent;
+  int                 mpiret;
+  size_t              countz;
+  p4est_locidx_t     *poff = data->poff;
+  p4est_locidx_t      pcount;
+
+  nlen = ((p4est_locidx_t) npe) * nlq;
+  for (li = 0; li < nlen; li++) {
+    inidx = local_en[li];
+    P4EST_ASSERT (inidx >= 0);
+    inode = (p4est_locidx_t *) sc_array_index (inodes, (size_t) inidx);
+    /* if this quadrant owns the node */
+    if (inode[0] == rank && inode[1] == li / npe) {
+      inode[0] = -1;
+      inode[1] = count++;
+    }
+  }
+  for (zz = 0; zz < inodes->elem_count; zz++) {
+    inode = (p4est_locidx_t *) sc_array_index (inodes, zz);
+    if (inode[0] >= 0) {
+      P4EST_ASSERT (inode[0] != rank);
+      poff[inode[0]]++;
+    }
+  }
+
+  pcount = 0;
+  for (i = 0; i < mpisize; i++) {
+    p4est_topidx_t      temp = pcount;
+
+    pcount += poff[i];
+    poff[i] = temp;
+  }
+  poff[mpisize] = pcount;
+
+  lnodes->owned_count = count;
+  lnodes->num_local_nodes = nln = (p4est_locidx_t) inodes->elem_count;
+  lnodes->nonlocal_nodes = P4EST_ALLOC (p4est_gloidx_t, nln - count);
+  memset (lnodes->nonlocal_nodes, -1,
+          (nln - count) * sizeof (p4est_gloidx_t));
+
+  num_send_procs = 0;
+  total_sent = 0;
+  sc_array_init (&(data->send_requests), sizeof (sc_MPI_Request));
+  data->send_buf = P4EST_ALLOC (sc_array_t, mpisize);
+  for (i = 0; i < mpisize; i++) {
+    sc_array_init (&(data->send_buf[i]), sizeof (p4est_locidx_t));
+  }
+  for (i = 0; i < mpisize; i++) {
+    send_info = &(send_buf_info[i]);
+    countz = send_info->elem_count;
+    if (countz > 0) {
+      P4EST_ASSERT (i != p4est->mpirank);
+      send = &(data->send_buf[i]);
+      for (zz = 0; zz < countz; zz++) {
+        binfo = (p4est_lnodes_buf_info_t *) sc_array_index (send_info, zz);
+        zindex = (size_t) binfo->first_index;
+        type = binfo->type;
+        if (type >= P4EST_LN_C_OFFSET) {
+          limit = nodes_per_corner;
+        }
+#ifdef P4_TO_P8
+        else if (type >= P8EST_LN_E_OFFSET) {
+          limit = nodes_per_edge;
+        }
+#endif
+        else {
+          P4EST_ASSERT (0 <= type && type < P4EST_FACES);
+          limit = nodes_per_face;
+        }
+        for (j = 0; j < limit; j++) {
+          lp = (p4est_locidx_t *) sc_array_push (send);
+          inode = (p4est_locidx_t *) sc_array_index (inodes, zindex++);
+          P4EST_ASSERT (inode[0] == -1 && inode[1] >= 0 && inode[1] < count);
+          *lp = inode[1];
+        }
+        if (binfo->send_sharers) {
+          lp = (p4est_locidx_t *) sc_array_push (send);
+          *lp = (p4est_locidx_t) binfo->share_count;
+          P4EST_ASSERT (binfo->share_count > 0);
+          zindex = (size_t) binfo->share_offset;
+          share_count = (int) binfo->share_count;
+          for (j = 0; j < share_count; j++) {
+            lp = (p4est_locidx_t *) sc_array_push (send);
+            share_proc = *((int *) sc_array_index (inode_sharers, zindex++));
+            *lp = (p4est_locidx_t) share_proc;
+            P4EST_ASSERT (0 <= share_proc && share_proc < mpisize);
+          }
+        }
+      }
+      send_count = send->elem_count;
+      send_request = (sc_MPI_Request *) sc_array_push (&data->send_requests);
+      mpiret = sc_MPI_Isend (send->array,
+                             (int) (send_count * sizeof (p4est_locidx_t)),
+                             sc_MPI_BYTE, i, P4EST_COMM_LNODES_PASS,
+                             p4est->mpicomm, send_request);
+      SC_CHECK_MPI (mpiret);
+      num_send_procs++;
+      total_sent += (send_count * sizeof (p4est_locidx_t));
+    }
+  }
+  P4EST_VERBOSEF ("Total of %lld bytes sent to %d processes\n",
+                  (unsigned long long) total_sent, num_send_procs);
+}
+
+#ifdef P4EST_ENABLE_DEBUG
+/* p4est_lnodes_test_comm:
+ *
+ * If the buf_info_t array is the same on both ends of a communication, then the
+ * information sent will be properly decoded.
+ *
+ */
+static              int8_t
+p4est_lnodes_test_comm (p4est_t * p4est, p4est_lnodes_data_t * data)
+{
+  int                 mpisize = p4est->mpisize;
+  int                 i, j;
+  sc_array_t         *send;
+  sc_array_t         *send_buf_info = data->send_buf_info;
+  sc_array_t         *recv, *recv2;
+  sc_array_t         *recv_buf;
+  sc_array_t         *recv_buf_info = data->recv_buf_info;
+  size_t              zz;
+  int                 mpiret;
+  sc_array_t          send_requests;
+  sc_MPI_Request     *send_request;
+  sc_MPI_Status       probe_status, recv_status;
+  int                 num_recv_procs = 0;
+  int                *num_recv_expect = P4EST_ALLOC_ZERO (int, mpisize);
+  int                 byte_count;
+  size_t              count, elem_count;
+  p4est_lnodes_buf_info_t *binfo, *binfo2;
+
+  sc_array_init (&send_requests, sizeof (sc_MPI_Request));
+  for (i = 0; i < mpisize; i++) {
+    send = &(send_buf_info[i]);
+    count = send->elem_count;
+    if (count > 0) {
+      P4EST_ASSERT (i != p4est->mpirank);
+      send_request = (sc_MPI_Request *) sc_array_push (&send_requests);
+      mpiret = sc_MPI_Isend (send->array,
+                             (int) (count * sizeof (p4est_lnodes_buf_info_t)),
+                             sc_MPI_BYTE, i, P4EST_COMM_LNODES_TEST,
+                             p4est->mpicomm, send_request);
+      SC_CHECK_MPI (mpiret);
+    }
+    recv = &(recv_buf_info[i]);
+    count = recv->elem_count;
+    if (count) {
+      P4EST_ASSERT (i != p4est->mpirank);
+      num_recv_procs++;
+      num_recv_expect[i]++;
+    }
+  }
+
+  recv_buf = P4EST_ALLOC (sc_array_t, mpisize);
+  for (i = 0; i < mpisize; i++) {
+    sc_array_init (&(recv_buf[i]), sizeof (p4est_lnodes_buf_info_t));
+  }
+  for (i = 0; i < num_recv_procs; i++) {
+    mpiret = sc_MPI_Probe (sc_MPI_ANY_SOURCE, P4EST_COMM_LNODES_TEST,
+                           p4est->mpicomm, &probe_status);
+    SC_CHECK_MPI (mpiret);
+    j = probe_status.MPI_SOURCE;
+    P4EST_ASSERT (j != p4est->mpirank && num_recv_expect[j] == 1);
+    recv = &(recv_buf[j]);
+    mpiret = sc_MPI_Get_count (&probe_status, sc_MPI_BYTE, &byte_count);
+    SC_CHECK_MPI (mpiret);
+    P4EST_ASSERT (byte_count % ((int) sizeof (p4est_lnodes_buf_info_t)) == 0);
+    elem_count = ((size_t) byte_count) / sizeof (p4est_lnodes_buf_info_t);
+    sc_array_resize (recv, elem_count);
+    mpiret = sc_MPI_Recv (recv->array, byte_count, sc_MPI_BYTE, j,
+                          P4EST_COMM_LNODES_TEST, p4est->mpicomm,
+                          &recv_status);
+    SC_CHECK_MPI (mpiret);
+    num_recv_expect[j]--;
+
+    recv2 = &(recv_buf_info[j]);
+    P4EST_ASSERT (recv2->elem_count == recv->elem_count);
+    for (zz = 0; zz < elem_count; zz++) {
+      binfo2 = (p4est_lnodes_buf_info_t *) sc_array_index (recv2, zz);
+      binfo = (p4est_lnodes_buf_info_t *) sc_array_index (recv, zz);
+      P4EST_ASSERT (binfo->type == binfo2->type);
+      P4EST_ASSERT (binfo->send_sharers == binfo2->send_sharers);
+      if (!binfo->send_sharers) {
+        P4EST_ASSERT (binfo->share_count == binfo2->share_count);
+      }
+    }
+  }
+
+  if (send_requests.elem_count > 0) {
+    mpiret = sc_MPI_Waitall ((int) send_requests.elem_count,
+                             (sc_MPI_Request *) send_requests.array,
+                             sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+  sc_array_reset (&send_requests);
+  for (i = 0; i < mpisize; i++) {
+    sc_array_reset (&(recv_buf[i]));
+  }
+
+  P4EST_FREE (recv_buf);
+  P4EST_FREE (num_recv_expect);
+  return 1;
+}
+#endif
+
+/* p4est_lnodes_recv:
+ *
+ * Each process has its sorted receive lists.
+ * Each process knows who to expect nodes from.
+ * When the nodes are received, they are put into a sorter list dedicated just
+ * to the process that the nodes come from, this is then sorted in ascending
+ * node order.
+ */
+static void
+p4est_lnodes_recv (p4est_t * p4est, p4est_lnodes_data_t * data,
+                   p4est_lnodes_t * lnodes)
+{
+  int                 mpisize = p4est->mpisize;
+  int                 i, j, k;
+  int                 limit;
+  sc_array_t         *recv, *recv_info;
+  sc_array_t         *recv_buf;
+  sc_array_t         *recv_buf_info = data->recv_buf_info;
+  size_t              count, info_count, zz;
+  int                 mpiret;
+  sc_MPI_Status       probe_status, recv_status;
+  int                 num_recv_procs = 0;
+  size_t              total_recv = 0;
+  int                *num_recv_expect = P4EST_ALLOC_ZERO (int, mpisize);
+  int                 byte_count;
+  size_t              elem_count;
+  p4est_lnodes_buf_info_t *binfo;
+  size_t              zindex;
+  int                 nodes_per_face = data->nodes_per_face;
+#ifdef P4_TO_P8
+  int                 nodes_per_edge = data->nodes_per_edge;
+#endif
+  int                 nodes_per_corner = data->nodes_per_corner;
+  p4est_locidx_t     *lp;
+  int                *ip;
+  p4est_locidx_t     *inode;
+  sc_array_t         *inode_sharers = data->inode_sharers;
+  sc_array_t         *inodes = data->inodes;
+  int                 share_count;
+  sc_array_t         *sorter;
+  p4est_gloidx_t     *nonlocal_nodes = lnodes->nonlocal_nodes;
+  p4est_locidx_t     *poff = data->poff;
+
+  for (i = 0; i < mpisize; i++) {
+    recv_info = &(recv_buf_info[i]);
+    count = recv_info->elem_count;
+    if (count) {
+      P4EST_ASSERT (i != p4est->mpirank);
+      P4EST_ASSERT (poff[i + 1] - poff[i] > 0);
+      num_recv_procs++;
+      num_recv_expect[i]++;
+    }
+  }
+
+  sorter = sc_array_new (2 * sizeof (p4est_locidx_t));
+
+  recv_buf = P4EST_ALLOC (sc_array_t, mpisize);
+  for (i = 0; i < mpisize; i++) {
+    sc_array_init (&(recv_buf[i]), sizeof (p4est_locidx_t));
+  }
+  for (i = 0; i < num_recv_procs; i++) {
+    mpiret = sc_MPI_Probe (sc_MPI_ANY_SOURCE, P4EST_COMM_LNODES_PASS,
+                           p4est->mpicomm, &probe_status);
+    SC_CHECK_MPI (mpiret);
+    j = probe_status.MPI_SOURCE;
+    P4EST_ASSERT (j != p4est->mpirank && num_recv_expect[j] == 1);
+    recv = &(recv_buf[j]);
+    recv_info = &(recv_buf_info[j]);
+    mpiret = sc_MPI_Get_count (&probe_status, sc_MPI_BYTE, &byte_count);
+    SC_CHECK_MPI (mpiret);
+    P4EST_ASSERT (byte_count % ((int) sizeof (p4est_locidx_t)) == 0);
+    elem_count = ((size_t) byte_count) / sizeof (p4est_locidx_t);
+    sc_array_resize (recv, elem_count);
+    mpiret = sc_MPI_Recv (recv->array, byte_count, sc_MPI_BYTE, j,
+                          P4EST_COMM_LNODES_PASS, p4est->mpicomm,
+                          &recv_status);
+    SC_CHECK_MPI (mpiret);
+    num_recv_expect[j]--;
+
+    info_count = recv_info->elem_count;
+    count = 0;
+    for (zz = 0; zz < info_count; zz++) {
+      binfo = (p4est_lnodes_buf_info_t *) sc_array_index (recv_info, zz);
+      if (binfo->type >= P4EST_LN_C_OFFSET) {
+        limit = nodes_per_corner;
+      }
+#ifdef P4_TO_P8
+      else if (binfo->type >= P8EST_LN_E_OFFSET) {
+        limit = nodes_per_edge;
+      }
+#endif
+      else {
+        limit = nodes_per_face;
+      }
+      zindex = (size_t) binfo->first_index;
+      for (k = 0; k < limit; k++) {
+        inode = (p4est_locidx_t *) sc_array_index (inodes, zindex);
+        lp = (p4est_locidx_t *) sc_array_index (recv, count++);
+        P4EST_ASSERT (inode[0] == j);
+        P4EST_ASSERT (*lp >= 0);
+        inode[1] = *lp;
+        lp = (p4est_locidx_t *) sc_array_push (sorter);
+        lp[0] = (p4est_locidx_t) inode[1];
+        lp[1] = (p4est_locidx_t) zindex++;
+      }
+      if (binfo->send_sharers) {
+        lp = (p4est_locidx_t *) sc_array_index (recv, count++);
+        share_count = (int) (*lp);
+        P4EST_ASSERT (share_count > 0);
+        P4EST_ASSERT (binfo->share_count == -1);
+        P4EST_ASSERT (binfo->share_offset == -1);
+        binfo->share_count = (int8_t) share_count;
+        binfo->share_offset = (p4est_locidx_t) inode_sharers->elem_count;
+        ip = (int *) sc_array_push_count (inode_sharers, share_count);
+        for (k = 0; k < share_count; k++) {
+          lp = (p4est_locidx_t *) sc_array_index (recv, count++);
+          ip[k] = (int) (*lp);
+          P4EST_ASSERT (0 <= *ip && *ip < mpisize);
+        }
+      }
+    }
+    P4EST_ASSERT (count == elem_count);
+    total_recv += byte_count;
+    P4EST_ASSERT ((p4est_locidx_t) sorter->elem_count ==
+                  poff[j + 1] - poff[j]);
+    sc_array_sort (sorter, p4est_locidx_compare);
+    for (zz = 0; zz < sorter->elem_count; zz++) {
+      lp = (p4est_locidx_t *) sc_array_index (sorter, zz);
+      nonlocal_nodes[poff[j] + zz] = lp[1];
+    }
+    sc_array_reset (sorter);
+  }
+
+  if (data->send_requests.elem_count > 0) {
+    mpiret = sc_MPI_Waitall ((int) data->send_requests.elem_count,
+                             (sc_MPI_Request *) data->send_requests.array,
+                             sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+  sc_array_reset (&data->send_requests);
+  for (i = 0; i < mpisize; i++) {
+    sc_array_reset (&(data->send_buf[i]));
+    sc_array_reset (&(recv_buf[i]));
+  }
+
+  P4EST_VERBOSEF ("Total of %lld bytes received from %d processes\n",
+                  (unsigned long long) total_recv, num_recv_procs);
+  P4EST_FREE (data->send_buf);
+  P4EST_FREE (recv_buf);
+  P4EST_FREE (num_recv_expect);
+  sc_array_destroy (sorter);
+}
+
+/* p4est_lnodes_global_and_sharers:
+ *
+ * Each process that owns a node shared by the local process has a list of nodes
+ * that is sorted in increasing local_index, while the local element nodes refer
+ * to indices in the inodes array.  A map between the two is created, which
+ * allows local element nodes to point to global nodes.  After this is done, the
+ * sharers can be created.
+ */
+static              p4est_gloidx_t
+p4est_lnodes_global_and_sharers (p4est_lnodes_data_t * data,
+                                 p4est_lnodes_t * lnodes, p4est_t * p4est)
+{
+  int                 i, j, k, l;
+  int                 mpisize = p4est->mpisize;
+  p4est_gloidx_t     *gnodes = lnodes->nonlocal_nodes, gtotal;
+  size_t              count, zz;
+  p4est_locidx_t     *lp, li, *inode;
+  sc_array_t         *inodes = data->inodes;
+  p4est_locidx_t     *elnodes = lnodes->element_nodes;
+  p4est_locidx_t      nlen = lnodes->num_local_elements * lnodes->vnodes;
+#ifdef P4EST_ENABLE_DEBUG
+  p4est_locidx_t      num_inodes = (p4est_locidx_t) data->inodes->elem_count;
+#endif
+  p4est_locidx_t      inidx;
+  int                *comm_proc;
+  int                 comm_proc_count;
+  sc_array_t         *inode_sharers = data->inode_sharers;
+  sc_array_t         *sharers;
+  p4est_lnodes_rank_t *lrank;
+  sc_array_t         *binfo_array;
+  p4est_lnodes_buf_info_t *binfo;
+  p4est_locidx_t      share_offset, owned_count = lnodes->owned_count;
+  int                 share_count;
+  int                 limit;
+  int                 nodes_per_face = data->nodes_per_face;
+  size_t              zindex;
+#ifdef P4_TO_P8
+  int                 nodes_per_edge = data->nodes_per_edge;
+#endif
+  int                 proc;
+  int                 shareidx;
+  p4est_locidx_t      gidx;
+  sc_array_t         *shared_nodes;
+  p4est_locidx_t     *global_num_indep;
+  p4est_gloidx_t     *global_offsets = P4EST_ALLOC (p4est_gloidx_t,
+                                                    mpisize + 1);
+  p4est_locidx_t     *poff = data->poff;
+
+  global_num_indep = lnodes->global_owned_count = P4EST_ALLOC (p4est_locidx_t,
+                                                               mpisize);
+  sc_MPI_Allgather (&owned_count, 1, P4EST_MPI_LOCIDX, global_num_indep, 1,
+                    P4EST_MPI_LOCIDX, p4est->mpicomm);
+
+  global_offsets[0] = 0;
+  for (i = 0; i < mpisize; i++) {
+    global_offsets[i + 1] = global_offsets[i] +
+      (p4est_gloidx_t) global_num_indep[i];
+  }
+  lnodes->global_offset = global_offsets[p4est->mpirank];
+  gtotal = global_offsets[p4est->mpisize];
+
+  i = p4est->mpirank;
+  for (i = 0; i < mpisize; i++) {
+    if (i == p4est->mpirank) {
+      continue;
+    }
+    for (j = poff[i]; j < poff[i + 1]; j++) {
+      li = gnodes[j];
+      inode = (p4est_locidx_t *) sc_array_index (inodes, li);
+      P4EST_ASSERT (inode[0] == i);
+      gnodes[j] = inode[1] + global_offsets[i];
+      inode[1] = j + owned_count;
+    }
+  }
+
+  for (li = 0; li < nlen; li++) {
+    inidx = elnodes[li];
+    P4EST_ASSERT (0 <= inidx && inidx < num_inodes);
+    inode = (p4est_locidx_t *) sc_array_index (inodes, (size_t) inidx);
+    if (inode[0] == -1) {
+      P4EST_ASSERT (0 <= inode[1] && inode[1] < lnodes->owned_count);
+      elnodes[li] = inode[1];
+    }
+    else {
+      P4EST_ASSERT (inode[0] >= 0 && inode[0] != p4est->mpirank &&
+                    inode[0] < mpisize);
+      P4EST_ASSERT (inode[1] >= poff[inode[0]] + owned_count &&
+                    inode[1] < poff[inode[0] + 1] + owned_count);
+      elnodes[li] = inode[1];
+    }
+  }
+
+  /* figure out all nodes that also share nodes shared by the local process */
+  comm_proc = P4EST_ALLOC_ZERO (int, mpisize);
+  count = inode_sharers->elem_count;
+  for (zz = 0; zz < count; zz++) {
+    i = *((int *) sc_array_index (inode_sharers, zz));
+    comm_proc[i] = 1;
+  }
+  /* create an entry in sharers for each such process, providing a map from
+   * process id to sharer index */
+  comm_proc_count = 0;
+  lnodes->sharers = sharers = sc_array_new (sizeof (p4est_lnodes_rank_t));
+  for (i = 0; i < mpisize; i++) {
+    if (comm_proc[i]) {
+      lrank = (p4est_lnodes_rank_t *) sc_array_push (sharers);
+      lrank->rank = i;
+      sc_array_init (&(lrank->shared_nodes), sizeof (p4est_locidx_t));
+      comm_proc[i] = comm_proc_count++;
+    }
+    else {
+      comm_proc[i] = -1;
+    }
+  }
+
+  /* for every node in a send or receive list, figure out which global node it
+   * is, and which processes share it, and add the index in global nodes to that
+   * sharer's element_nodes array.
+   */
+  for (i = 0; i < mpisize; i++) {
+    for (j = 0; j < 2; j++) {
+      if (j == 0) {
+        binfo_array = &(data->send_buf_info[i]);
+      }
+      else {
+        binfo_array = &(data->recv_buf_info[i]);
+      }
+      count = binfo_array->elem_count;
+      for (zz = 0; zz < count; zz++) {
+        binfo = (p4est_lnodes_buf_info_t *) sc_array_index (binfo_array, zz);
+        if (binfo->type >= P4EST_LN_C_OFFSET) {
+          limit = 1;
+        }
+#ifdef P4_TO_P8
+        else if (binfo->type >= P8EST_LN_E_OFFSET) {
+          limit = nodes_per_edge;
+        }
+#endif
+        else {
+          limit = nodes_per_face;
+        }
+        zindex = (size_t) binfo->first_index;
+        share_offset = binfo->share_offset;
+        share_count = (int) binfo->share_count;
+        for (k = 0; k < limit; k++) {
+          inode = (p4est_locidx_t *) sc_array_index (inodes, zindex++);
+          gidx = inode[1];
+          if (j == 0) {
+            P4EST_ASSERT (inode[0] == -1);
+            P4EST_ASSERT (gidx < owned_count);
+            shareidx = comm_proc[i];
+            P4EST_ASSERT (shareidx >= 0);
+            lrank = p4est_lnodes_rank_array_index_int (sharers, shareidx);
+            P4EST_ASSERT (lrank->rank == i);
+            lp = (p4est_locidx_t *) sc_array_push (&(lrank->shared_nodes));
+            *lp = gidx;
+
+            P4EST_ASSERT (share_count >= 2);
+            proc = *((int *) sc_array_index (inode_sharers,
+                                             (size_t) share_offset + 1));
+            P4EST_ASSERT (proc != p4est->mpirank);
+            if (proc == i) {
+              shareidx = comm_proc[p4est->mpirank];
+              P4EST_ASSERT (shareidx >= 0);
+              lrank = p4est_lnodes_rank_array_index_int (sharers, shareidx);
+              P4EST_ASSERT (lrank->rank == p4est->mpirank);
+              lp = (p4est_locidx_t *) sc_array_push (&(lrank->shared_nodes));
+              *lp = gidx;
+            }
+          }
+          else {
+            P4EST_ASSERT (inode[0] == i);
+            P4EST_ASSERT (poff[i] <= inode[1] - owned_count &&
+                          inode[1] - owned_count < poff[i + 1]);
+            for (l = 0; l < share_count; l++) {
+              proc = *((int *) sc_array_index (inode_sharers,
+                                               (size_t) share_offset +
+                                               (size_t) l));
+              shareidx = comm_proc[proc];
+              P4EST_ASSERT (shareidx >= 0);
+              lrank = p4est_lnodes_rank_array_index_int (sharers, shareidx);
+              P4EST_ASSERT (lrank->rank == proc);
+              lp = (p4est_locidx_t *) sc_array_push (&(lrank->shared_nodes));
+              *lp = gidx;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /* for each sharer, figure out which entries in element_nodes are owned by
+   * the current process, and which are owned by the sharer's rank */
+  for (i = 0; i < comm_proc_count; i++) {
+    lrank = p4est_lnodes_rank_array_index_int (sharers, i);
+    shared_nodes = &(lrank->shared_nodes);
+    count = shared_nodes->elem_count;
+    if (count) {
+      sc_array_t         *sortshared =
+        sc_array_new_size (2 * sizeof (p4est_gloidx_t),
+                           count);
+      for (zz = 0; zz < count; zz++) {
+        p4est_gloidx_t     *gp;
+
+        gidx = *((p4est_locidx_t *) sc_array_index (shared_nodes, zz));
+        gp = (p4est_gloidx_t *) sc_array_index (sortshared, zz);
+        gp[0] = p4est_lnodes_global_index (lnodes, gidx);
+        gp[1] = gidx;
+      }
+      sc_array_sort (sortshared, p4est_gloidx_compare);
+      for (zz = 0; zz < count; zz++) {
+        p4est_gloidx_t     *gp;
+
+        gp = (p4est_gloidx_t *) sc_array_index (sortshared, zz);
+        *((p4est_locidx_t *) sc_array_index (shared_nodes, zz)) = gp[1];
+      }
+      sc_array_destroy (sortshared);
+    }
+    proc = lrank->rank;
+    lrank->shared_mine_offset = -1;
+    lrank->shared_mine_count = 0;
+    for (zz = 0; zz < count; zz++) {
+      gidx = *((p4est_locidx_t *) sc_array_index (shared_nodes, zz));
+      if (gidx < lnodes->owned_count) {
+        if (lrank->shared_mine_count == 0) {
+          lrank->shared_mine_offset = (p4est_locidx_t) zz;
+        }
+        lrank->shared_mine_count++;
+      }
+    }
+    if (proc == p4est->mpirank) {
+      lrank->owned_count = lnodes->owned_count;
+      lrank->owned_offset = 0;
+    }
+    else {
+      lrank->owned_offset = poff[proc] + owned_count;
+      lrank->owned_count = poff[proc + 1] - poff[proc];
+      P4EST_VERBOSEF ("Processor %d shares %llu nodes with processor %d\n",
+                      p4est->mpirank, (unsigned long long) count,
+                      lrank->rank);
+      P4EST_VERBOSEF ("Processor %d owns %d nodes used by processor %d\n",
+                      p4est->mpirank, lrank->shared_mine_count, lrank->rank);
+      P4EST_VERBOSEF ("Processor %d borrows %d nodes from processor %d\n",
+                      p4est->mpirank, lrank->owned_count, lrank->rank);
+    }
+  }
+  P4EST_FREE (comm_proc);
+  P4EST_FREE (global_offsets);
+
+  return gtotal;
+}
+
+p4est_lnodes_t     *
+p4est_lnodes_new (p4est_t * p4est, p4est_ghost_t * ghost_layer, int degree)
+{
+  p4est_iter_face_t   fiter;
+  p4est_iter_volume_t viter;
+  p4est_iter_corner_t citer;
+#ifdef P4_TO_P8
+  p8est_iter_edge_t   eiter;
+#endif
+  p4est_lnodes_data_t data;
+  p4est_locidx_t      nel;
+  p4est_locidx_t      nlen;
+#ifdef P4EST_ENABLE_DEBUG
+  p4est_locidx_t      lj;
+#endif
+  p4est_lnodes_t     *lnodes = P4EST_ALLOC (p4est_lnodes_t, 1);
+  p4est_gloidx_t      gtotal;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING "_lnodes_new, degree %d\n",
+                            degree);
+  p4est_log_indent_push ();
+
+#ifndef P4_TO_P8
+  P4EST_ASSERT (degree >= 1 || degree == -1 || degree == -P4EST_DIM);
+#else
+  P4EST_ASSERT (degree >= 1 || degree == -1 ||
+                degree == -2 || degree == -P4EST_DIM);
+#endif
+
+  lnodes->mpicomm = p4est->mpicomm;
+  lnodes->degree = degree;
+  lnodes->num_local_elements = nel = p4est->local_num_quadrants;
+  if (degree > 0) {
+#ifndef P4_TO_P8
+    lnodes->vnodes = (degree + 1) * (degree + 1);
+#else
+    lnodes->vnodes = (degree + 1) * (degree + 1) * (degree + 1);
+#endif
+  }
+  else if (degree == -1) {
+    lnodes->vnodes = P4EST_FACES;
+  }
+#ifdef P4_TO_P8
+  else if (degree == -2) {
+    lnodes->vnodes = P4EST_FACES + P8EST_EDGES;
+  }
+#endif
+  else if (degree == -P4EST_DIM) {
+    lnodes->vnodes = P4EST_FACES +
+#ifdef P4_TO_P8
+      P8EST_EDGES +
+#endif
+      P4EST_CHILDREN;
+  }
+  lnodes->face_code = P4EST_ALLOC_ZERO (p4est_lnodes_code_t, nel);
+  nlen = nel * lnodes->vnodes;
+  lnodes->element_nodes = P4EST_ALLOC (p4est_locidx_t, nlen);
+  memset (lnodes->element_nodes, -1, nlen * sizeof (p4est_locidx_t));
+
+  p4est_lnodes_init_data (&data, degree, p4est, ghost_layer, lnodes);
+  viter = data.nodes_per_volume ? p4est_lnodes_volume_callback : NULL;
+  fiter = data.nodes_per_face ? p4est_lnodes_face_callback :
+    ((data.nodes_per_corner ||
+#ifdef P4_TO_P8
+      data.nodes_per_edge ||
+#endif
+      0) ? p4est_lnodes_face_simple_callback : NULL);
+#ifdef P4_TO_P8
+  eiter = data.nodes_per_edge ? p8est_lnodes_edge_callback :
+    (data.nodes_per_corner ? p8est_lnodes_edge_simple_callback_void : NULL);
+#endif
+  citer = data.nodes_per_corner ? p4est_lnodes_corner_callback : NULL;
+
+  p4est_iterate_ext (p4est, ghost_layer, &data, viter, fiter,
+#ifdef P4_TO_P8
+                     eiter,
+#endif
+                     citer, 1);
+
+#ifdef P4EST_ENABLE_DEBUG
+  for (lj = 0; lj < nlen; lj++) {
+    P4EST_ASSERT (lnodes->element_nodes[lj] >= 0);
+  }
+#endif
+
+  P4EST_ASSERT (p4est_lnodes_test_comm (p4est, &data));
+
+  p4est_lnodes_count_send (&data, p4est, lnodes);
+
+  p4est_lnodes_recv (p4est, &data, lnodes);
+
+  gtotal = p4est_lnodes_global_and_sharers (&data, lnodes, p4est);
+
+  p4est_lnodes_reset_data (&data, p4est);
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done " P4EST_STRING "_lnodes_new with"
+                            " %lld global nodes\n",
+                            (unsigned long long) gtotal);
+  return lnodes;
+}
+
+void
+p4est_lnodes_destroy (p4est_lnodes_t * lnodes)
+{
+  size_t              zz, count;
+  p4est_lnodes_rank_t *lrank;
+
+  P4EST_FREE (lnodes->element_nodes);
+  P4EST_FREE (lnodes->nonlocal_nodes);
+  P4EST_FREE (lnodes->global_owned_count);
+  P4EST_FREE (lnodes->face_code);
+
+  count = lnodes->sharers->elem_count;
+  for (zz = 0; zz < count; zz++) {
+    lrank = p4est_lnodes_rank_array_index (lnodes->sharers, zz);
+    sc_array_reset (&(lrank->shared_nodes));
+  }
+  sc_array_destroy (lnodes->sharers);
+
+  P4EST_FREE (lnodes);
+}
+
+#ifdef P4EST_ENABLE_MPI
+
+static              size_t
+ghost_tree_type (sc_array_t * array, size_t zindex, void *data)
+{
+  p4est_quadrant_t   *q;
+
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_quadrant_t));
+
+  q = (p4est_quadrant_t *) sc_array_index (array, zindex);
+  return (size_t) q->p.which_tree;
+}
+
+#endif /* P4EST_ENABLE_MPI */
+
+void
+p4est_ghost_support_lnodes (p4est_t * p4est, p4est_lnodes_t * lnodes,
+                            p4est_ghost_t * ghost)
+{
+#ifdef P4EST_ENABLE_MPI
+  sc_array_t         *ghosts = &ghost->ghosts;
+  sc_array_t         *mirrors = &ghost->mirrors;
+  p4est_locidx_t     *proc_offsets = ghost->proc_offsets;
+  p4est_locidx_t     *tree_offsets = ghost->tree_offsets;
+  p4est_locidx_t     *mirror_proc_offsets = ghost->mirror_proc_offsets;
+  p4est_locidx_t     *mirror_proc_mirrors = ghost->mirror_proc_mirrors;
+  p4est_locidx_t     *mirror_tree_offsets = ghost->mirror_tree_offsets;
+  sc_array_t         *new_ghosts, *new_mirrors;
+  int                 mpisize = p4est->mpisize;
+  int                 self = p4est->mpirank;
+  int                 mpiret;
+  int                 n_comm;
+  p4est_locidx_t      old_num_mirrors, *newmpcount;
+  sc_array_t         *recv_counts, *send_counts;
+  sc_array_t         *recv_all, *send_all;
+  sc_array_t         *recv_requests;
+  sc_array_t         *send_requests;
+  p4est_connectivity_t *conn = p4est->connectivity;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING "_ghost_support_lnodes %s\n",
+                            p4est_connect_type_string (ghost->btype));
+  p4est_log_indent_push ();
+
+  /* this should only be done with an unexpanded ghost layer */
+  P4EST_ASSERT (ghost->mirror_proc_fronts == ghost->mirror_proc_mirrors &&
+                ghost->mirror_proc_front_offsets ==
+                ghost->mirror_proc_front_offsets);
+
+  /* get the topological nodes */
+  n_comm = lnodes->sharers ? (int) lnodes->sharers->elem_count : 0;
+  recv_counts = sc_array_new_size (sizeof (p4est_locidx_t), n_comm);
+  send_counts = sc_array_new_size (sizeof (p4est_locidx_t), n_comm);
+  recv_all = sc_array_new_size (sizeof (sc_array_t), n_comm);
+  send_all = sc_array_new_size (sizeof (sc_array_t), n_comm);
+  recv_requests = sc_array_new (sizeof (sc_MPI_Request));
+  send_requests = sc_array_new (sizeof (sc_MPI_Request));
+
+  new_mirrors =
+    sc_array_new_size (sizeof (p4est_quadrant_t), mirrors->elem_count);
+  old_num_mirrors = (p4est_locidx_t) mirrors->elem_count;
+  sc_array_copy (new_mirrors, mirrors);
+
+  /* figure out which quads to send and send them */
+  {
+    int                *quads_per_node;
+    int                *tmp_count;
+    p4est_quadrant_t   *node_to_quad;
+    p4est_locidx_t     *qpn_offsets;
+    int                 V, vid;
+    p4est_locidx_t      nid, elid, N, K;
+    p4est_lnodes_rank_t *lrank;
+    int                 i, p;
+    p4est_topidx_t      flt, llt, t;
+
+    N = lnodes->num_local_nodes;
+    K = lnodes->num_local_elements;
+    V = lnodes->vnodes;
+    /* count the quadrants per node */
+    quads_per_node = P4EST_ALLOC_ZERO (int, N);
+    for (elid = 0; elid < K; elid++) {
+      for (vid = 0; vid < V; vid++) {
+        nid = lnodes->element_nodes[V * elid + vid];
+        quads_per_node[nid]++;
+      }
+    }
+
+    /* convert counts to offsets */
+    qpn_offsets = P4EST_ALLOC (int, N + 1);
+    qpn_offsets[0] = 0;
+    for (nid = 0; nid < N; nid++) {
+      qpn_offsets[nid + 1] = qpn_offsets[nid] + quads_per_node[nid];
+    }
+
+    /* create and fill the node to quadrant array */
+    node_to_quad = P4EST_ALLOC (p4est_quadrant_t, qpn_offsets[N]);
+    tmp_count = P4EST_ALLOC_ZERO (int, N);
+
+    flt = p4est->first_local_tree;
+    llt = p4est->last_local_tree;
+
+    for (elid = 0, t = flt; t <= llt; t++) {
+      p4est_tree_t       *tree = p4est_tree_array_index (p4est->trees, t);
+      sc_array_t         *quadrants = &tree->quadrants;
+      p4est_locidx_t      il, nquads = (p4est_locidx_t) quadrants->elem_count;
+
+      for (il = 0; il < nquads; il++, elid++) {
+        for (vid = 0; vid < V; vid++) {
+          p4est_quadrant_t   *q;
+
+          nid = lnodes->element_nodes[V * elid + vid];
+          q = &(node_to_quad[qpn_offsets[nid] + tmp_count[nid]++]);
+          *q = *p4est_quadrant_array_index (quadrants, (size_t) il);
+          q->p.piggy3.which_tree = t;
+          q->p.piggy3.local_num = elid;
+        }
+      }
+    }
+
+    newmpcount = P4EST_ALLOC (p4est_locidx_t, p4est->mpisize);
+    for (i = 0; i < p4est->mpisize; i++) {
+      newmpcount[i] = mirror_proc_offsets[i + 1] - mirror_proc_offsets[i];
+    }
+
+    /* for each communication partner */
+    for (i = 0; i < n_comm; i++) {
+      p4est_locidx_t     *count_recv, *count_send;
+      sc_MPI_Request     *req;
+      p4est_locidx_t      nshared, il, jl, nquads;
+      sc_array_t         *shared, *send_quads;
+      sc_array_t          mirror_view;
+      p4est_locidx_t      startmirror, endmirror;
+
+      lrank = p4est_lnodes_rank_array_index_int (lnodes->sharers, i);
+
+      p = lrank->rank;
+      if (p == self) {
+        continue;
+      }
+      /* post the count receive */
+      count_recv = (p4est_locidx_t *) sc_array_index_int (recv_counts, i);
+      req = (sc_MPI_Request *) sc_array_push (recv_requests);
+      mpiret = sc_MPI_Irecv (count_recv, 1, P4EST_MPI_LOCIDX, p,
+                             P4EST_COMM_GHOST_SUPPORT_COUNT, p4est->mpicomm,
+                             req);
+      SC_CHECK_MPI (mpiret);
+
+      /* create the send buffer */
+      shared = &(lrank->shared_nodes);
+      nshared = (p4est_locidx_t) shared->elem_count;
+      send_quads = (sc_array_t *) sc_array_index_int (send_all, i);
+      sc_array_init (send_quads, sizeof (p4est_quadrant_t));
+      for (il = 0; il < nshared; il++) {
+        p4est_locidx_t      startquad, endquad, qid;
+
+        nid = *((p4est_locidx_t *) sc_array_index (shared, il));
+        startquad = qpn_offsets[nid];
+        endquad = qpn_offsets[nid + 1];
+        for (qid = startquad; qid < endquad; qid++) {
+          p4est_quadrant_t   *q;
+
+          q = p4est_quadrant_array_push (send_quads);
+          *q = node_to_quad[qid];
+        }
+      }
+      sc_array_sort (send_quads, p4est_quadrant_compare_piggy);
+      sc_array_uniq (send_quads, p4est_quadrant_compare_piggy);
+
+      nquads = (p4est_locidx_t) send_quads->elem_count;
+
+      startmirror = mirror_proc_offsets[p];
+      endmirror = mirror_proc_offsets[p + 1];
+      sc_array_init_data (&mirror_view,
+                          &(mirror_proc_mirrors[startmirror]),
+                          sizeof (p4est_locidx_t),
+                          (size_t) (endmirror - startmirror));
+
+      for (il = 0, jl = 0; (size_t) il < send_quads->elem_count; il++) {
+        p4est_quadrant_t   *q;
+        ssize_t             idx;
+        int                 already_sent = 0;
+
+        q = p4est_quadrant_array_index (send_quads, (size_t) il);
+        idx = sc_array_bsearch (mirrors, q, p4est_quadrant_compare_piggy);
+        if (idx >= 0) {
+          p4est_locidx_t      key = (p4est_locidx_t) idx;
+          idx = sc_array_bsearch (&mirror_view, &key, p4est_locidx_compare);
+          if (idx >= 0) {
+            already_sent = 1;
+          }
+        }
+        else {
+          p4est_quadrant_t   *q2;
+
+          q2 = p4est_quadrant_array_push (new_mirrors);
+          *q2 = *q;
+        }
+        if (already_sent) {
+          nquads--;
+        }
+        else {
+          size_t              jz;
+
+          jz = (size_t) jl++;
+          if (jz != (size_t) il) {
+            p4est_quadrant_t   *q2;
+
+            q2 = p4est_quadrant_array_index (send_quads, jz);
+            *q2 = *q;
+          }
+        }
+      }
+      sc_array_resize (send_quads, nquads);
+      newmpcount[p] += nquads;
+
+      P4EST_LDEBUGF ("ghost layer support nodes sending %lld new to %d\n",
+                     (long long) nquads, p);
+
+      count_send = (p4est_locidx_t *) sc_array_index_int (send_counts, i);
+      *count_send = nquads;
+      req = (sc_MPI_Request *) sc_array_push (send_requests);
+      mpiret = sc_MPI_Isend (count_send, 1, P4EST_MPI_LOCIDX, p,
+                             P4EST_COMM_GHOST_SUPPORT_COUNT, p4est->mpicomm,
+                             req);
+      SC_CHECK_MPI (mpiret);
+
+      if (nquads) {
+        req = (sc_MPI_Request *) sc_array_push (send_requests);
+        mpiret = sc_MPI_Isend (send_quads->array, nquads * sizeof
+                               (p4est_quadrant_t), sc_MPI_BYTE, p,
+                               P4EST_COMM_GHOST_SUPPORT_LOAD, p4est->mpicomm,
+                               req);
+        SC_CHECK_MPI (mpiret);
+      }
+    }
+
+    P4EST_FREE (quads_per_node);
+    P4EST_FREE (tmp_count);
+    P4EST_FREE (node_to_quad);
+    P4EST_FREE (qpn_offsets);
+  }
+
+  /* update mirror structures */
+  {
+    p4est_locidx_t      new_num_mirrors;
+    p4est_locidx_t     *newmpoffset, *new_mirror_proc_mirrors;
+    int                 i, p;
+    p4est_lnodes_rank_t *lrank;
+
+    newmpoffset = P4EST_ALLOC (p4est_locidx_t, mpisize + 1);
+    newmpoffset[0] = 0;
+    for (i = 0; i < mpisize; i++) {
+      newmpoffset[i + 1] = newmpoffset[i] + newmpcount[i];
+    }
+    new_mirror_proc_mirrors =
+      P4EST_ALLOC (p4est_locidx_t, newmpoffset[mpisize]);
+
+    sc_array_sort (new_mirrors, p4est_quadrant_compare_piggy);
+    sc_array_uniq (new_mirrors, p4est_quadrant_compare_piggy);
+    new_num_mirrors = (p4est_locidx_t) new_mirrors->elem_count;
+    P4EST_ASSERT (new_num_mirrors >= old_num_mirrors);
+
+    if (new_num_mirrors > old_num_mirrors) {
+      sc_array_t          split;
+      p4est_topidx_t      t;
+
+      /* update mirror_tree_offsets */
+      sc_array_init (&split, sizeof (size_t));
+      sc_array_split (new_mirrors, &split,
+                      (size_t) conn->num_trees, ghost_tree_type, NULL);
+      P4EST_ASSERT (split.elem_count == (size_t) conn->num_trees + 1);
+      for (t = 0; t <= conn->num_trees; ++t) {
+        size_t             *ppz;
+
+        ppz = (size_t *) sc_array_index (&split, (size_t) t);
+        mirror_tree_offsets[t] = (p4est_locidx_t) (*ppz);
+      }
+      sc_array_reset (&split);
+    }
+
+    /* update mirror_proc_mirrors */
+    for (p = 0; p < mpisize; p++) {
+      p4est_locidx_t      old_offset = mirror_proc_offsets[p];
+      p4est_locidx_t      old_count = mirror_proc_offsets[p + 1] - old_offset;
+      p4est_locidx_t      il;
+
+      for (il = 0; il < old_count; il++) {
+        ssize_t             idx;
+        p4est_quadrant_t   *q;
+
+        q = p4est_quadrant_array_index (mirrors, (size_t)
+                                        mirror_proc_mirrors[old_offset + il]);
+
+        idx = sc_array_bsearch (new_mirrors, q, p4est_quadrant_compare_piggy);
+        P4EST_ASSERT (idx >= 0);
+        new_mirror_proc_mirrors[newmpoffset[p] + il] = (p4est_locidx_t) idx;
+      }
+    }
+
+    for (i = 0; i < n_comm; i++) {
+      sc_array_t         *send_quads;
+      int                 p;
+
+      lrank = p4est_lnodes_rank_array_index_int (lnodes->sharers, i);
+
+      p = lrank->rank;
+      if (p == self) {
+        continue;
+      }
+      send_quads = (sc_array_t *) sc_array_index_int (send_all, i);
+      if (send_quads->elem_count) {
+        p4est_locidx_t      il, startidx, endidx;
+        p4est_locidx_t      old_offset = mirror_proc_offsets[p];
+        p4est_locidx_t      old_count =
+          mirror_proc_offsets[p + 1] - old_offset;
+        sc_array_t          pview;
+
+        startidx = newmpoffset[p];
+        endidx = newmpoffset[p + 1];
+
+        for (il = 0; (size_t) il < send_quads->elem_count; il++) {
+          p4est_quadrant_t   *q;
+          ssize_t             idx;
+          p4est_locidx_t      idxl;
+
+          q = p4est_quadrant_array_index (send_quads, (size_t) il);
+          idx =
+            sc_array_bsearch (new_mirrors, q, p4est_quadrant_compare_piggy);
+          P4EST_ASSERT (idx >= 0);
+          idxl = (p4est_locidx_t) idx;
+          new_mirror_proc_mirrors[startidx + old_count + il] = idxl;
+        }
+
+        sc_array_init_data (&pview, &new_mirror_proc_mirrors[startidx],
+                            sizeof (p4est_locidx_t),
+                            (size_t) (endidx - startidx));
+        sc_array_sort (&pview, p4est_locidx_compare);
+        sc_array_reset (&pview);
+      }
+    }
+    P4EST_FREE (mirror_proc_mirrors);
+    P4EST_FREE (mirror_proc_offsets);
+    ghost->mirror_proc_mirrors = mirror_proc_mirrors =
+      new_mirror_proc_mirrors;
+    ghost->mirror_proc_offsets = mirror_proc_offsets = newmpoffset;
+    ghost->mirror_proc_fronts = mirror_proc_mirrors;
+    ghost->mirror_proc_front_offsets = mirror_proc_offsets;
+
+    sc_array_resize (mirrors, new_mirrors->elem_count);
+    sc_array_copy (mirrors, new_mirrors);
+    sc_array_destroy (new_mirrors);
+    P4EST_FREE (newmpcount);
+  }
+
+  /* update the ghost layer */
+  {
+    int                 i, p;
+    p4est_locidx_t     *new_proc_counts, *new_proc_offsets;
+    p4est_lnodes_rank_t *lrank;
+
+    mpiret =
+      sc_MPI_Waitall ((int) recv_requests->elem_count,
+                      (sc_MPI_Request *) recv_requests->array,
+                      sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+    sc_array_reset (recv_requests);
+    sc_array_init (recv_requests, sizeof (sc_MPI_Request));
+    new_proc_counts = P4EST_ALLOC (p4est_locidx_t, mpisize);
+    for (p = 0; p < mpisize; p++) {
+      new_proc_counts[p] = proc_offsets[p + 1] - proc_offsets[p];
+    }
+
+    for (i = 0; i < n_comm; i++) {
+      sc_array_t         *recv_quads;
+      p4est_locidx_t      recv_count;
+
+      lrank = p4est_lnodes_rank_array_index_int (lnodes->sharers, i);
+
+      p = lrank->rank;
+      if (p == self) {
+        continue;
+      }
+
+      recv_count = *((p4est_locidx_t *) sc_array_index_int (recv_counts, i));
+
+      P4EST_LDEBUGF
+        ("ghost layer support nodes receiving  %lld new from %d\n",
+         (long long) recv_count, p);
+
+      new_proc_counts[p] += recv_count;
+
+      recv_quads = (sc_array_t *) sc_array_index (recv_all, i);
+      sc_array_init_size (recv_quads, sizeof (p4est_quadrant_t),
+                          (size_t) recv_count);
+      if (recv_count) {
+        sc_MPI_Request     *req;
+
+        req = (sc_MPI_Request *) sc_array_push (recv_requests);
+
+        mpiret =
+          sc_MPI_Irecv (recv_quads->array,
+                        recv_count * sizeof (p4est_quadrant_t), sc_MPI_BYTE,
+                        p, P4EST_COMM_GHOST_SUPPORT_LOAD, p4est->mpicomm,
+                        req);
+        SC_CHECK_MPI (mpiret);
+      }
+    }
+
+    new_proc_offsets = P4EST_ALLOC (p4est_locidx_t, mpisize + 1);
+    new_proc_offsets[0] = 0;
+    for (p = 0; p < mpisize; p++) {
+      new_proc_offsets[p + 1] = new_proc_offsets[p] + new_proc_counts[p];
+    }
+
+    new_ghosts = sc_array_new_size (sizeof (p4est_quadrant_t),
+                                    (size_t) new_proc_offsets[mpisize]);
+    for (p = 0; p < mpisize; p++) {
+      p4est_quadrant_t   *dest, *src;
+      p4est_locidx_t      count;
+
+      count = proc_offsets[p + 1] - proc_offsets[p];
+      if (count) {
+        dest = p4est_quadrant_array_index (new_ghosts, new_proc_offsets[p]);
+        src = p4est_quadrant_array_index (ghosts, proc_offsets[p]);
+        memcpy (dest, src, count * sizeof (p4est_quadrant_t));
+      }
+    }
+
+    mpiret =
+      sc_MPI_Waitall ((int) recv_requests->elem_count,
+                      (sc_MPI_Request *) recv_requests->array,
+                      sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+
+    for (i = 0; i < n_comm; i++) {
+      sc_array_t         *recv_quads;
+
+      lrank = p4est_lnodes_rank_array_index_int (lnodes->sharers, i);
+
+      p = lrank->rank;
+      if (p == self) {
+        continue;
+      }
+
+      recv_quads = (sc_array_t *) sc_array_index (recv_all, i);
+
+      if (recv_quads->elem_count) {
+        p4est_quadrant_t   *dest, *src;
+        p4est_locidx_t      startidx, endidx, oldcount;
+        sc_array_t          pview;
+
+        startidx = new_proc_offsets[p];
+        endidx = new_proc_offsets[p + 1];
+
+        oldcount = proc_offsets[p + 1] - proc_offsets[p];
+
+        dest = p4est_quadrant_array_index (new_ghosts, (size_t)
+                                           (new_proc_offsets[p] + oldcount));
+
+        src = p4est_quadrant_array_index (recv_quads, 0);
+
+        memcpy (dest, src,
+                recv_quads->elem_count * sizeof (p4est_quadrant_t));
+
+        sc_array_init_view (&pview, new_ghosts, startidx,
+                            (size_t) (endidx - startidx));
+        sc_array_sort (&pview, p4est_quadrant_compare_piggy);
+        sc_array_reset (&pview);
+      }
+    }
+
+    if (new_ghosts->elem_count > ghosts->elem_count) {
+      p4est_topidx_t      t;
+      sc_array_t          split;
+
+      /* update tree_offsets */
+      sc_array_init (&split, sizeof (size_t));
+      sc_array_split (new_ghosts, &split,
+                      (size_t) conn->num_trees, ghost_tree_type, NULL);
+      P4EST_ASSERT (split.elem_count == (size_t) conn->num_trees + 1);
+      for (t = 0; t <= conn->num_trees; ++t) {
+        size_t             *ppz;
+
+        ppz = (size_t *) sc_array_index (&split, (size_t) t);
+        tree_offsets[t] = *ppz;
+      }
+      sc_array_reset (&split);
+
+      P4EST_ASSERT (sc_array_is_sorted
+                    (new_ghosts, p4est_quadrant_compare_piggy));
+#ifdef P4EST_ENABLE_DEBUG
+      {
+        size_t              zz;
+
+        for (zz = 0; zz < ghosts->elem_count - 1; zz++) {
+          p4est_quadrant_t   *q1 = p4est_quadrant_array_index (ghosts, zz);
+          p4est_quadrant_t   *q2 =
+            p4est_quadrant_array_index (ghosts, zz + 1);
+
+          if (!p4est_quadrant_compare_piggy (q1, q2)) {
+            P4EST_LERROR ("already duplicate in ghost:\n");
+            p4est_quadrant_print (SC_LP_ERROR, q1);
+          }
+        }
+        for (zz = 0; zz < new_ghosts->elem_count - 1; zz++) {
+          p4est_quadrant_t   *q1 =
+            p4est_quadrant_array_index (new_ghosts, zz);
+          p4est_quadrant_t   *q2 =
+            p4est_quadrant_array_index (new_ghosts, zz + 1);
+
+          if (!p4est_quadrant_compare_piggy (q1, q2)) {
+            P4EST_LERROR ("duplicate in new ghost:\n");
+            p4est_quadrant_print (SC_LP_ERROR, q1);
+          }
+        }
+      }
+#endif
+      sc_array_resize (ghosts, new_ghosts->elem_count);
+      sc_array_copy (ghosts, new_ghosts);
+    }
+    P4EST_FREE (proc_offsets);
+    ghost->proc_offsets = new_proc_offsets;
+    sc_array_destroy (new_ghosts);
+    P4EST_FREE (new_proc_counts);
+
+    /* end sends */
+    mpiret =
+      sc_MPI_Waitall ((int) send_requests->elem_count,
+                      (sc_MPI_Request *) send_requests->array,
+                      sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+
+    /* clean up */
+    sc_array_destroy (recv_counts);
+    sc_array_destroy (send_counts);
+    for (i = 0; i < n_comm; i++) {
+      sc_array_t         *quads;
+
+      lrank = p4est_lnodes_rank_array_index_int (lnodes->sharers, i);
+
+      p = lrank->rank;
+      if (p == self) {
+        continue;
+      }
+
+      quads = (sc_array_t *) sc_array_index (recv_all, i);
+      sc_array_reset (quads);
+      quads = (sc_array_t *) sc_array_index (send_all, i);
+      sc_array_reset (quads);
+    }
+    sc_array_destroy (recv_all);
+    sc_array_destroy (send_all);
+    sc_array_destroy (recv_requests);
+    sc_array_destroy (send_requests);
+  }
+
+  P4EST_ASSERT (p4est_ghost_is_valid (p4est, ghost));
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_ghost_support_lnodes\n");
+#endif
+}
+
+p4est_lnodes_buffer_t *
+p4est_lnodes_share_owned_begin (sc_array_t * node_data,
+                                p4est_lnodes_t * lnodes)
+{
+  int                 mpiret;
+  int                 p, proc;
+  sc_array_t         *sharers = lnodes->sharers;
+  int                 npeers = (int) sharers->elem_count;
+  p4est_lnodes_rank_t *lrank;
+  sc_array_t         *requests;
+  sc_MPI_Request     *request;
+  p4est_locidx_t      li, lz;
+  void               *dest;
+  sc_array_t         *send_bufs;
+  sc_array_t         *send_buf;
+  p4est_locidx_t      mine_offset, mine_count;
+  size_t              elem_size = node_data->elem_size;
+  p4est_lnodes_buffer_t *buffer;
+  sc_MPI_Comm         comm = lnodes->mpicomm;
+  int                 mpirank;
+
+  P4EST_ASSERT (node_data->elem_count == (size_t) lnodes->num_local_nodes);
+
+  mpiret = sc_MPI_Comm_rank (comm, &mpirank);
+
+  buffer = P4EST_ALLOC (p4est_lnodes_buffer_t, 1);
+
+  buffer->requests = requests = sc_array_new (sizeof (sc_MPI_Request));
+  buffer->send_buffers = send_bufs = sc_array_new (sizeof (sc_array_t));
+  /* in this routine, the values from other processes are written directly
+   * into node_data */
+  buffer->recv_buffers = NULL;
+
+  for (p = 0; p < npeers; p++) {
+    lrank = p4est_lnodes_rank_array_index_int (sharers, p);
+    proc = lrank->rank;
+    if (proc == mpirank) {
+      continue;
+    }
+    if (lrank->owned_count) {
+      request = (sc_MPI_Request *) sc_array_push (requests);
+      mpiret =
+        sc_MPI_Irecv (node_data->array + elem_size * lrank->owned_offset,
+                      (int) (lrank->owned_count * elem_size), sc_MPI_BYTE,
+                      proc, P4EST_COMM_LNODES_OWNED, comm, request);
+      SC_CHECK_MPI (mpiret);
+    }
+    mine_count = lrank->shared_mine_count;
+    if (mine_count) {
+      mine_offset = lrank->shared_mine_offset;
+      send_buf = (sc_array_t *) sc_array_push (send_bufs);
+      sc_array_init (send_buf, elem_size);
+      sc_array_resize (send_buf, mine_count);
+      for (li = 0; li < mine_count; li++) {
+        lz = *((p4est_locidx_t *) sc_array_index (&lrank->shared_nodes,
+                                                  (size_t) (li +
+                                                            mine_offset)));
+        dest = sc_array_index (send_buf, (size_t) li);
+        memcpy (dest, node_data->array + elem_size * lz, elem_size);
+      }
+      request = (sc_MPI_Request *) sc_array_push (requests);
+      mpiret = sc_MPI_Isend (send_buf->array, (int) (mine_count * elem_size),
+                             sc_MPI_BYTE, proc, P4EST_COMM_LNODES_OWNED,
+                             comm, request);
+      SC_CHECK_MPI (mpiret);
+    }
+  }
+
+  return buffer;
+}
+
+void
+p4est_lnodes_share_owned_end (p4est_lnodes_buffer_t * buffer)
+{
+  int                 mpiret;
+  size_t              zz;
+  sc_array_t         *requests = buffer->requests;
+  sc_array_t         *send_bufs = buffer->send_buffers;
+  sc_array_t         *send_buf;
+
+  P4EST_ASSERT (buffer->recv_buffers == NULL);
+
+  if (requests->elem_count) {
+    mpiret = sc_MPI_Waitall ((int) requests->elem_count,
+                             (sc_MPI_Request *) requests->array,
+                             sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+  sc_array_destroy (requests);
+  for (zz = 0; zz < send_bufs->elem_count; zz++) {
+    send_buf = (sc_array_t *) sc_array_index (send_bufs, zz);
+    sc_array_reset (send_buf);
+  }
+  sc_array_destroy (send_bufs);
+  buffer->requests = NULL;
+  buffer->send_buffers = NULL;
+}
+
+void
+p4est_lnodes_share_owned (sc_array_t * array, p4est_lnodes_t * lnodes)
+{
+  p4est_lnodes_buffer_t *buffer;
+
+  buffer = p4est_lnodes_share_owned_begin (array, lnodes);
+  p4est_lnodes_share_owned_end (buffer);
+  p4est_lnodes_buffer_destroy (buffer);
+}
+
+p4est_lnodes_buffer_t *
+p4est_lnodes_share_all_begin (sc_array_t * node_data, p4est_lnodes_t * lnodes)
+{
+  int                 mpiret;
+  int                 p, proc;
+  sc_array_t         *sharers = lnodes->sharers;
+  int                 npeers = (int) sharers->elem_count;
+  p4est_lnodes_rank_t *lrank;
+  p4est_lnodes_buffer_t *buffer;
+  sc_array_t         *requests;
+  sc_MPI_Request     *request;
+  sc_array_t         *send_bufs;
+  sc_array_t         *send_buf;
+  sc_array_t         *recv_bufs;
+  sc_array_t         *recv_buf;
+  p4est_locidx_t      lz;
+  void               *dest;
+  size_t              zz;
+  size_t              count;
+  size_t              elem_size = node_data->elem_size;
+  sc_MPI_Comm         comm = lnodes->mpicomm;
+  int                 mpirank;
+
+  mpiret = sc_MPI_Comm_rank (comm, &mpirank);
+
+  P4EST_ASSERT (node_data->elem_count == (size_t) lnodes->num_local_nodes);
+
+  buffer = P4EST_ALLOC (p4est_lnodes_buffer_t, 1);
+  buffer->requests = requests = sc_array_new (sizeof (sc_MPI_Request));
+  buffer->send_buffers = send_bufs = sc_array_new (sizeof (sc_array_t));
+  buffer->recv_buffers = recv_bufs = sc_array_new (sizeof (sc_array_t));
+  sc_array_resize (recv_bufs, (size_t) npeers);
+  sc_array_resize (send_bufs, (size_t) npeers);
+
+  for (p = 0; p < npeers; p++) {
+    lrank = p4est_lnodes_rank_array_index_int (sharers, p);
+    proc = lrank->rank;
+    if (proc == mpirank) {
+      /* there is no buffer for the current process: look in node_data
+       * for values */
+      recv_buf = (sc_array_t *) sc_array_index_int (recv_bufs, p);
+      sc_array_init (recv_buf, elem_size);
+      send_buf = (sc_array_t *) sc_array_index_int (send_bufs, p);
+      sc_array_init (send_buf, elem_size);
+      continue;
+    }
+    count = lrank->shared_nodes.elem_count;
+    if (lrank->shared_nodes.elem_count) {
+      recv_buf = (sc_array_t *) sc_array_index_int (recv_bufs, p);
+      sc_array_init (recv_buf, elem_size);
+      sc_array_resize (recv_buf, count);
+      request = (sc_MPI_Request *) sc_array_push (requests);
+      mpiret = sc_MPI_Irecv (recv_buf->array, (int) (count * elem_size),
+                             sc_MPI_BYTE, proc, P4EST_COMM_LNODES_ALL,
+                             comm, request);
+      SC_CHECK_MPI (mpiret);
+
+      send_buf = (sc_array_t *) sc_array_index_int (send_bufs, p);
+      sc_array_init (send_buf, elem_size);
+      sc_array_resize (send_buf, count);
+      for (zz = 0; zz < count; zz++) {
+        lz = *((p4est_locidx_t *) sc_array_index (&lrank->shared_nodes, zz));
+        dest = sc_array_index (send_buf, zz);
+        memcpy (dest, node_data->array + elem_size * lz, elem_size);
+      }
+      request = (sc_MPI_Request *) sc_array_push (requests);
+      mpiret = sc_MPI_Isend (send_buf->array, (int) (count * elem_size),
+                             sc_MPI_BYTE, proc, P4EST_COMM_LNODES_ALL,
+                             comm, request);
+      SC_CHECK_MPI (mpiret);
+    }
+  }
+
+  return buffer;
+}
+
+void
+p4est_lnodes_share_all_end (p4est_lnodes_buffer_t * buffer)
+{
+  int                 mpiret;
+  size_t              zz;
+  sc_array_t         *requests = buffer->requests;
+  sc_array_t         *send_bufs = buffer->send_buffers;
+  sc_array_t         *send_buf;
+
+  if (requests->elem_count) {
+    mpiret = sc_MPI_Waitall ((int) requests->elem_count,
+                             (sc_MPI_Request *) requests->array,
+                             sc_MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+  sc_array_destroy (requests);
+  for (zz = 0; zz < send_bufs->elem_count; zz++) {
+    send_buf = (sc_array_t *) sc_array_index (send_bufs, zz);
+    sc_array_reset (send_buf);
+  }
+  sc_array_destroy (send_bufs);
+  buffer->requests = NULL;
+  buffer->send_buffers = NULL;
+}
+
+p4est_lnodes_buffer_t *
+p4est_lnodes_share_all (sc_array_t * node_data, p4est_lnodes_t * lnodes)
+{
+  p4est_lnodes_buffer_t *buffer;
+
+  buffer = p4est_lnodes_share_all_begin (node_data, lnodes);
+  p4est_lnodes_share_all_end (buffer);
+
+  return buffer;
+}
+
+void
+p4est_lnodes_buffer_destroy (p4est_lnodes_buffer_t * buffer)
+{
+  int                 i;
+  size_t              zz;
+  sc_array_t         *requests = buffer->requests;
+  sc_array_t         *send_bufs = buffer->send_buffers;
+  sc_array_t         *recv_bufs = buffer->recv_buffers;
+  sc_array_t         *bufs, *buf;
+
+  if (requests != NULL) {
+    P4EST_ASSERT (requests->elem_size == sizeof (sc_MPI_Request));
+    sc_array_destroy (requests);
+  }
+  for (i = 0; i < 2; i++) {
+    bufs = (i == 0) ? send_bufs : recv_bufs;
+    if (bufs == NULL) {
+      continue;
+    }
+    P4EST_ASSERT (bufs->elem_size == sizeof (sc_array_t));
+    for (zz = 0; zz < bufs->elem_count; zz++) {
+      buf = (sc_array_t *) sc_array_index (bufs, zz);
+      sc_array_reset (buf);
+    }
+    sc_array_destroy (bufs);
+  }
+  P4EST_FREE (buffer);
+}
diff --git a/src/p4est_lnodes.h b/src/p4est_lnodes.h
new file mode 100644
index 0000000..4b90fc4
--- /dev/null
+++ b/src/p4est_lnodes.h
@@ -0,0 +1,365 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_LNODES_H
+#define P4EST_LNODES_H
+
+#include <p4est.h>
+#include <p4est_ghost.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef int8_t      p4est_lnodes_code_t;
+
+/** Store a parallel numbering of Lobatto points of a given degree > 0.
+ *
+ * Each element has degree+1 nodes per face
+ * and vnodes = (degree+1)^2 nodes per volume.
+ * num_local_elements is the number of local quadrants in the p4est.
+ * element_nodes is of dimension vnodes * num_local_elements and lists the
+ * nodes of each element in lexicographic yx-order (x varies fastest); so for
+ * degree == 2, this is the layout of nodes:
+ *
+ *         f_3
+ *  c_2           c_3
+ *      6---7---8
+ *      |       |
+ * f_0  3   4   5  f_1
+ *      |       |
+ *      0---1---2
+ *  c_0           c_1
+ *         f_2
+ *
+ * element_nodes indexes into the set of local nodes, layed out as follows:
+ * local nodes = [<-----owned_count----->|<-----nonlocal_nodes----->]
+ *             = [<----------------num_local_nodes----------------->]
+ * nonlocal_nodes contains the globally unique numbers for independent nodes
+ * that are owned by other processes; for local nodes, the globally unique
+ * numbers are given by i + global_offset, where i is the local number.
+ * Hanging nodes are always local and don't have a global number.
+ * They index the geometrically corresponding independent nodes of a neighbor.
+ *
+ * Whether nodes are hanging or not is decided based on the element faces.
+ * This information is encoded in face_code with one int8_t per element.
+ * If no faces are hanging, the value is zero, otherwise the face_code is
+ * interpreted by p4est_lnodes_decode.
+ *
+ * Independent nodes can be shared by multiple MPI ranks.
+ * The owner rank of a node is the one from the lowest numbered element
+ * on the lowest numbered octree *touching* the node.
+ *
+ * What is meant by *touching*?
+ * A quadrant is said to touch all faces/corners that are incident on it,
+ * and by extension all nodes that are contained in those faces/corners.
+ *
+ *         X +-----------+
+ *         o |           |
+ *         o |           |
+ * +-----+ o |     p     |
+ * |  q  | o |           |
+ * |     | o |           |
+ * +-----+ O +-----------+
+ *
+ * In this example degree = 6.  There are 5 nodes that live on the face
+ * between q and p, and one at each corner of that face.  The face is incident
+ * on q, so q owns the nodes on the face (provided q is from a lower tree or
+ * has a lower index than p).  The lower corner is incident on q, so q owns it
+ * as well.  The upper corner is not incident on q, so q cannot own it.
+ *
+ * global_owned_count contains the number of independent nodes owned by each
+ * process.
+ *
+ * The sharers array contains items of type p4est_lnodes_rank_t
+ * that hold the ranks that own or share independent local nodes.
+ * If there are no shared nodes on this processor, it is empty.
+ * Otherwise, it is sorted by rank and the current process is included.
+ *
+ * degree < 0 indicates that the lnodes data structure is being used to number
+ * the quadrant boundary object (faces and corners) rather than the $C^0$
+ * Lobatto nodes:
+ *
+ * if degree == -1, then one node is assigned per face, and no nodes are
+ * assigned per volume or per corner: this numbering can be used for low-order
+ * Raviart-Thomas elements.  In this case, vnodes == 4, and the nodes are
+ * listed in face-order:
+ *
+ *         f_3
+ *  c_2           c_3
+ *      +---3---+
+ *      |       |
+ * f_0  0       1  f_1
+ *      |       |
+ *      +---2---+
+ *  c_0           c_1
+ *         f_2
+ *
+ * if degree == -2, then one node is assigned per face and per corner and no
+ * nodes are assigned per volume.  In this case, vnodes == 8, and the
+ * nodes are listed in face-order, followed by corner-order:
+ *
+ *         f_3
+ *  c_2           c_3
+ *      6---3---7
+ *      |       |
+ * f_0  0       1  f_1
+ *      |       |
+ *      4---2---5
+ *  c_0           c_1
+ *         f_2
+ *
+ */
+typedef struct p4est_lnodes
+{
+  sc_MPI_Comm         mpicomm;
+  p4est_locidx_t      num_local_nodes;
+  p4est_locidx_t      owned_count;
+  p4est_gloidx_t      global_offset;
+  p4est_gloidx_t     *nonlocal_nodes;
+  sc_array_t         *sharers;
+  p4est_locidx_t     *global_owned_count;
+
+  int                 degree, vnodes;
+  p4est_locidx_t      num_local_elements;
+  p4est_lnodes_code_t *face_code;
+  p4est_locidx_t     *element_nodes;
+}
+p4est_lnodes_t;
+
+/** The structure stored in the sharers array.
+ *
+ * shared_nodes is a sorted array of p4est_locidx_t
+ * that indexes into local nodes.  The shared_nodes array has a
+ * contiguous (or empty) section of nodes owned by the current rank.
+ * shared_mine_offset and shared_mine_count identify this section
+ * by indexing the shared_nodes array, not the local nodes array.
+ * owned_offset and owned_count define the section of local nodes
+ * that is owned by the listed rank (the section may be empty).
+ * For the current process these coincide with those in p4est_lnodes_t.
+ */
+typedef struct p4est_lnodes_rank
+{
+  int                 rank;
+  sc_array_t          shared_nodes;
+  p4est_locidx_t      shared_mine_offset, shared_mine_count;
+  p4est_locidx_t      owned_offset, owned_count;
+}
+p4est_lnodes_rank_t;
+
+/** Decode the face_code into hanging face information.
+ *
+ * This is mostly for demonstration purposes.  Applications probably will
+ * integrate it into their own loop over the face for performance reasons.
+ *
+ * \param[in] face_code as in the p4est_lnodes_t structure.
+ * \param[out] hanging face: if there are hanging faces,
+ *             hanging_face = -1 if the face is not hanging,
+ *                          = 0 if the face is the first half,
+ *                          = 1 if the face is the second half.
+ *             note: not touched if there are no hanging faces.
+ * \return              true if any face is hanging, false otherwise.
+ */
+/*@unused@*/
+static inline int
+p4est_lnodes_decode (p4est_lnodes_code_t face_code, int hanging_face[4])
+{
+  P4EST_ASSERT (face_code >= 0);
+
+  if (face_code) {
+    int                 i;
+    int8_t              c = face_code & 0x03;
+    int                 f;
+    int8_t              work = face_code >> 2;
+
+    memset (hanging_face, -1, 4 * sizeof (int));
+
+    for (i = 0; i < 2; ++i) {
+      f = p4est_corner_faces[c][i];
+      hanging_face[f] = (work & 0x01) ? p4est_corner_face_corners[c][f] : -1;
+      work >>= 1;
+    }
+
+    return 1;
+  }
+  else {
+    return 0;
+  }
+}
+
+p4est_lnodes_t     *p4est_lnodes_new (p4est_t * p4est,
+                                      p4est_ghost_t * ghost_layer,
+                                      int degree);
+
+void                p4est_lnodes_destroy (p4est_lnodes_t * lnodes);
+
+/** Expand the ghost layer to include the support of all nodes supported on
+ * the local partition.
+ *
+ * \param [in]     p4est        The forest from which the ghost layer was
+ *                              generated.
+ * \param [in]     lnodes       The nodes to support.
+ * \param [in,out] ghost        The ghost layer to be expanded.
+ */
+void                p4est_ghost_support_lnodes (p4est_t * p4est,
+                                                p4est_lnodes_t * lnodes,
+                                                p4est_ghost_t * ghost);
+
+/** Expand the ghost layer as in p4est_ghost_expand(), but use node support to
+ * define adjacency instead of geometric adjacency.
+ *
+ * \param [in]     p4est        The forest from which the ghost layer was
+ *                              generated.
+ * \param [in]     lnodes       The nodes to support.
+ * \param [in,out] ghost        The ghost layer to be expanded.
+ */
+void                p4est_ghost_expand_by_lnodes (p4est_t * p4est,
+                                                  p4est_lnodes_t * lnodes,
+                                                  p4est_ghost_t * ghost);
+
+/** p4est_lnodes_buffer_t handles the communication of data associated with
+ * nodes.
+ *
+ * \a send_buffers is an array of arrays: one buffer for each process to which
+ * the current process sends node-data.  It should not be altered between
+ * a shared_*_begin and a shared_*_end call.
+ *
+ * \a recv_buffers is an array of arrays that is used in lnodes_share_all_*.
+ * \a recv_buffers[j] corresponds with lnodes->sharers[j]: it is the same
+ * length as \a lnodes->sharers[j]->shared_nodes.  At the completion of
+ * lnodes_share_all or lnodes_share_all_end, recv_buffers[j] contains the
+ * node-data from the process lnodes->sharers[j]->rank
+ * (unless j is the current rank, in which case recv_buffers[j] is empty).
+ */
+typedef struct p4est_lnodes_buffer
+{
+  sc_array_t         *requests; /* sc_MPI_Request */
+  sc_array_t         *send_buffers;
+  sc_array_t         *recv_buffers;
+}
+p4est_lnodes_buffer_t;
+
+/** p4est_lnodes_share_owned_begin
+ *
+ * \a node_data is a user-defined array of arbitrary type, where each entry
+ * is associated with the \a lnodes local nodes entry of matching index.
+ * For every local nodes entry that is owned by a process
+ * other than the current one, the value in the \a node_data array of the
+ * owning process is written directly into the \a node_data array of the current
+ * process.  Values of \a node_data are not guaranteed to be sent or received
+ * until the \a buffer created by p4est_lnodes_share_owned_begin is passed to
+ * p4est_lnodes_share_owned_end.
+ *
+ * To be memory neutral, the \a buffer created by
+ * p4est_lnodes_share_owned_begin must be destroying with
+ * p4est_lnodes_buffer_destroy (it is not destroyed by
+ * p4est_lnodes_share_owned_end).
+ */
+p4est_lnodes_buffer_t *p4est_lnodes_share_owned_begin (sc_array_t * node_data,
+                                                       p4est_lnodes_t *
+                                                       lnodes);
+
+void                p4est_lnodes_share_owned_end (p4est_lnodes_buffer_t *
+                                                  buffer);
+
+/** Equivalent to calling p4est_lnodes_share_owned_end directly after
+ * p4est_lnodes_share_owned_begin.  Use if there is no local work that can be
+ * done to mask the communication cost.
+ */
+void                p4est_lnodes_share_owned (sc_array_t * node_data,
+                                              p4est_lnodes_t * lnodes);
+
+/** p4est_lnodes_share_all_begin
+ *
+ * \a node_data is a user_defined array of arbitrary type, where each entry
+ * is associated with the lnodes local nodes entry of matching index.
+ * For every process that shares an entry with the current one, the value in
+ * the \a node_data array of that process is written into a
+ * \a buffer->recv_buffers entry as described above.  The user can then perform
+ * some arbitrary work that requires the data from all processes that share a
+ * node (such as reduce, max, min, etc.).  When the work concludes, the
+ * \a buffer should be destroyed with p4est_lnodes_buffer_destroy.
+ *
+ * Values of \a node_data are not guaranteed to be sent, and
+ * \a buffer->recv_buffer entries are not guaranteed to be received until
+ * the \a buffer created by p4est_lnodes_share_all_begin is passed to
+ * p4est_lnodes_share_all_end.
+ */
+p4est_lnodes_buffer_t *p4est_lnodes_share_all_begin (sc_array_t * node_data,
+                                                     p4est_lnodes_t * lnodes);
+
+void                p4est_lnodes_share_all_end (p4est_lnodes_buffer_t *
+                                                buffer);
+
+/** Equivalent to calling p4est_lnodes_share_all_end directly after
+ * p4est_lnodes_share_all_begin.  Use if there is no local work that can be
+ * done to mask the communication cost.
+ * \return          A fully initialized buffer that contains the received data.
+ *                  After processing this data, the buffer must be freed with
+ *                  p4est_lnodes_buffer_destroy.
+ */
+p4est_lnodes_buffer_t *p4est_lnodes_share_all (sc_array_t * node_data,
+                                               p4est_lnodes_t * lnodes);
+
+void                p4est_lnodes_buffer_destroy (p4est_lnodes_buffer_t *
+                                                 buffer);
+
+/** Return a pointer to a lnodes_rank array element indexed by a int.
+ */
+/*@unused@*/
+static inline p4est_lnodes_rank_t *
+p4est_lnodes_rank_array_index_int (sc_array_t * array, int it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_lnodes_rank_t));
+  P4EST_ASSERT (it >= 0 && (size_t) it < array->elem_count);
+
+  return (p4est_lnodes_rank_t *)
+    (array->array + sizeof (p4est_lnodes_rank_t) * it);
+}
+
+/** Return a pointer to a lnodes_rank array element indexed by a size_t.
+ */
+/*@unused@*/
+static inline p4est_lnodes_rank_t *
+p4est_lnodes_rank_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_lnodes_rank_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p4est_lnodes_rank_t *)
+    (array->array + sizeof (p4est_lnodes_rank_t) * it);
+}
+
+/** Compute the global number of a local node number */
+/*@unused@*/
+static inline       p4est_gloidx_t
+p4est_lnodes_global_index (p4est_lnodes_t * lnodes, p4est_locidx_t lidx)
+{
+  p4est_locidx_t      owned = lnodes->owned_count;
+  P4EST_ASSERT (lidx >= 0 && lidx < lnodes->num_local_nodes);
+
+  return (lidx < owned) ? lnodes->global_offset + lidx :
+    lnodes->nonlocal_nodes[lidx - owned];
+}
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_LNODES */
diff --git a/src/p4est_mesh.c b/src/p4est_mesh.c
new file mode 100644
index 0000000..6948d2c
--- /dev/null
+++ b/src/p4est_mesh.c
@@ -0,0 +1,858 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_iterate.h>
+#include <p4est_mesh.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_iterate.h>
+#include <p8est_mesh.h>
+#endif
+
+/** For a quadrant that touches a tree face with a corner inside the face,
+ * get the number of the touching face.
+ */
+static int
+tree_face_quadrant_corner_face (const p4est_quadrant_t * q, int corner)
+{
+  int                 which;
+  p4est_qcoord_t      end = P4EST_LAST_OFFSET (q->level);
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+  P4EST_ASSERT (0 <= corner && corner < P4EST_CHILDREN);
+
+  which = corner & 1;
+  if (q->x == (which ? end : 0)) {
+    return which;
+  }
+  which = corner & 2;
+  if (q->y == (which ? end : 0)) {
+    return 2 + (which >> 1);
+  }
+#ifdef P4_TO_P8
+  which = corner & 4;
+  if (q->z == (which ? end : 0)) {
+    return 4 + (which >> 2);
+  }
+#endif
+  SC_ABORT_NOT_REACHED ();
+}
+
+static              p4est_locidx_t
+mesh_corner_allocate (p4est_mesh_t * mesh, p4est_locidx_t clen,
+                      p4est_locidx_t ** pcquad, int8_t ** pccorner)
+{
+  p4est_locidx_t      cornerid, cstart, cend;
+
+  P4EST_ASSERT (clen > 0);
+  P4EST_ASSERT (mesh->corner_offset->elem_count ==
+                (size_t) (mesh->local_num_corners + 1));
+
+  cornerid = mesh->local_num_corners++;
+  cstart = *(p4est_locidx_t *) sc_array_index (mesh->corner_offset, cornerid);
+  cend = cstart + clen;
+  *(p4est_locidx_t *) sc_array_push (mesh->corner_offset) = cend;
+
+  P4EST_ASSERT (mesh->corner_offset->elem_count ==
+                (size_t) (mesh->local_num_corners + 1));
+
+  P4EST_ASSERT (mesh->corner_quad->elem_count == (size_t) cstart);
+  *pcquad = (p4est_locidx_t *) sc_array_push_count (mesh->corner_quad, clen);
+  P4EST_ASSERT (mesh->corner_quad->elem_count == (size_t) cend);
+
+  P4EST_ASSERT (mesh->corner_corner->elem_count == (size_t) cstart);
+  *pccorner = (int8_t *) sc_array_push_count (mesh->corner_corner, clen);
+  P4EST_ASSERT (mesh->corner_corner->elem_count == (size_t) cend);
+
+  return cornerid;
+}
+
+static void
+mesh_iter_corner (p4est_iter_corner_info_t * info, void *user_data)
+{
+  int                 i, j;
+  int                 f1, f2, code, orientation;
+  int                 fc1, fc2, diagonal;
+#ifdef P4_TO_P8
+  int                 pref, pset;
+#endif
+  int                 visited[P4EST_CHILDREN];
+  int8_t             *pccorner;
+  size_t              cz, zz;
+  p4est_locidx_t      qoffset, qid1, qid2;
+  p4est_locidx_t      cornerid_offset, cornerid;
+  p4est_locidx_t     *pcquad;
+  p4est_mesh_t       *mesh = (p4est_mesh_t *) user_data;
+  p4est_iter_corner_side_t *side1, *side2;
+  p4est_tree_t       *tree1, *tree2;
+
+  /* Check the case when the corner does not involve neighbors */
+  cz = info->sides.elem_count;
+  P4EST_ASSERT (cz > 0);
+  P4EST_ASSERT (info->tree_boundary || cz == P4EST_CHILDREN);
+  if (cz == 1) {
+    return;
+  }
+
+  if (info->tree_boundary == P4EST_CONNECT_FACE) {
+    /* This corner is inside an inter-tree face */
+    if (cz == P4EST_HALF) {
+      /* This is a tree face boundary, no corner neighbors exist */
+      return;
+    }
+    P4EST_ASSERT (cz == P4EST_CHILDREN);
+    cornerid_offset = mesh->local_num_quadrants + mesh->ghost_num_quadrants;
+
+    /* Process a corner in pairs of diagonal inter-tree neighbors */
+    memset (visited, 0, P4EST_CHILDREN * sizeof (int));
+    for (i = 0; i < P4EST_HALF; ++i) {
+      side1 = side2 = NULL;
+      f1 = -1;
+      fc1 = -1;
+      qid1 = -3;
+      for (j = 0; j < P4EST_CHILDREN; ++j) {
+        if (visited[j]) {
+          continue;
+        }
+
+        /* Remember the first side we want to pair up */
+        if (side1 == NULL) {
+          side1 =
+            (p4est_iter_corner_side_t *) sc_array_index_int (&info->sides, j);
+          P4EST_ASSERT (side1->quad != NULL);
+          f1 = tree_face_quadrant_corner_face (side1->quad, side1->corner);
+          fc1 = p4est_corner_face_corners[side1->corner][f1];
+          P4EST_ASSERT (0 <= fc1 && fc1 < P4EST_HALF);
+          tree1 = p4est_tree_array_index (info->p4est->trees, side1->treeid);
+          qid1 = side1->quadid + (side1->is_ghost ? mesh->local_num_quadrants
+                                  : tree1->quadrants_offset);
+          visited[j] = 1;
+          continue;
+        }
+
+        /* Examine a potential second side */
+        P4EST_ASSERT (side2 == NULL);
+        side2 =
+          (p4est_iter_corner_side_t *) sc_array_index_int (&info->sides, j);
+        P4EST_ASSERT (side2->quad != NULL);
+        f2 = tree_face_quadrant_corner_face (side2->quad, side2->corner);
+        if (side1->treeid == side2->treeid && f1 == f2) {
+          /* Periodicity allows for equal trees and unequal faces */
+          side2 = NULL;
+          continue;
+        }
+
+        /* This side as in the opposite tree */
+        fc2 = p4est_corner_face_corners[side2->corner][f2];
+        P4EST_ASSERT (0 <= fc2 && fc2 < P4EST_HALF);
+        code = info->p4est->connectivity->tree_to_face[P4EST_FACES *
+                                                       side1->treeid + f1];
+        orientation = code / P4EST_FACES;
+        P4EST_ASSERT (f2 == code % P4EST_FACES);
+#ifdef P4_TO_P8
+        pref = p8est_face_permutation_refs[f1][f2];
+        pset = p8est_face_permutation_sets[pref][orientation];
+        diagonal = (p8est_face_permutations[pset][fc1] ^ fc2) == 3;
+#else
+        diagonal = (fc1 ^ fc2) != orientation;
+#endif
+        if (!diagonal) {
+          side2 = NULL;
+          continue;
+        }
+
+        /* We have found a diagonally opposite second side */
+        tree2 = p4est_tree_array_index (info->p4est->trees, side2->treeid);
+        qid2 = side2->quadid + (side2->is_ghost ? mesh->local_num_quadrants
+                                : tree2->quadrants_offset);
+        if (!side1->is_ghost) {
+          P4EST_ASSERT (0 <= qid1 && qid1 < mesh->local_num_quadrants);
+          P4EST_ASSERT (mesh->quad_to_corner[P4EST_CHILDREN * qid1 +
+                                             side1->corner] == -1);
+          cornerid = mesh_corner_allocate (mesh, 1, &pcquad, &pccorner);
+          mesh->quad_to_corner[P4EST_CHILDREN * qid1 + side1->corner] =
+            cornerid_offset + cornerid;
+          *pcquad = qid2;
+          *pccorner = side2->corner;
+        }
+        if (!side2->is_ghost) {
+          P4EST_ASSERT (0 <= qid2 && qid2 < mesh->local_num_quadrants);
+          P4EST_ASSERT (mesh->quad_to_corner[P4EST_CHILDREN * qid2 +
+                                             side2->corner] == -1);
+          cornerid = mesh_corner_allocate (mesh, 1, &pcquad, &pccorner);
+          mesh->quad_to_corner[P4EST_CHILDREN * qid2 + side2->corner] =
+            cornerid_offset + cornerid;
+          *pcquad = qid1;
+          *pccorner = side1->corner;
+        }
+        visited[j] = 1;
+        break;
+      }
+      P4EST_ASSERT (side1 != NULL && side2 != NULL);
+    }
+    return;
+  }
+
+#ifdef P4_TO_P8
+  if (info->tree_boundary == P8EST_CONNECT_EDGE) {
+    /* Tree corner neighbors across an edge are not implemented: set to -2 */
+    for (zz = 0; zz < cz; ++zz) {
+      side1 = (p4est_iter_corner_side_t *) sc_array_index (&info->sides, zz);
+      if (!side1->is_ghost) {
+        tree1 = p4est_tree_array_index (info->p4est->trees, side1->treeid);
+        qid1 = side1->quadid + tree1->quadrants_offset;
+        P4EST_ASSERT (0 <= qid1 && qid1 < mesh->local_num_quadrants);
+        P4EST_ASSERT (mesh->quad_to_corner[P4EST_CHILDREN * qid1 +
+                                           side1->corner] == -1);
+        mesh->quad_to_corner[P4EST_CHILDREN * qid1 + side1->corner] = -2;
+      }
+    }
+    return;
+  }
+#endif
+
+  if (info->tree_boundary == P4EST_CONNECT_CORNER) {
+    /* True tree corner neighbors are not implemented yet: set to -2 */
+    for (zz = 0; zz < cz; ++zz) {
+      side1 = (p4est_iter_corner_side_t *) sc_array_index (&info->sides, zz);
+      if (!side1->is_ghost) {
+        tree1 = p4est_tree_array_index (info->p4est->trees, side1->treeid);
+        qid1 = side1->quadid + tree1->quadrants_offset;
+        P4EST_ASSERT (0 <= qid1 && qid1 < mesh->local_num_quadrants);
+        P4EST_ASSERT (mesh->quad_to_corner[P4EST_CHILDREN * qid1 +
+                                           side1->corner] == -1);
+        mesh->quad_to_corner[P4EST_CHILDREN * qid1 + side1->corner] = -2;
+      }
+    }
+    return;
+  }
+
+  /* Process a corner inside the tree in pairs of diagonal neighbors */
+  P4EST_ASSERT (!info->tree_boundary);
+  side1 = (p4est_iter_corner_side_t *) sc_array_index (&info->sides, 0);
+  tree1 = p4est_tree_array_index (info->p4est->trees, side1->treeid);
+  qoffset = tree1->quadrants_offset;
+  memset (visited, 0, P4EST_CHILDREN * sizeof (int));
+  for (i = 0; i < P4EST_HALF; ++i) {
+    side1 = side2 = NULL;
+    qid1 = -3;
+    for (j = 0; j < P4EST_CHILDREN; ++j) {
+      if (visited[j]) {
+        continue;
+      }
+
+      /* Remember the first side we want to pair up */
+      if (side1 == NULL) {
+        side1 =
+          (p4est_iter_corner_side_t *) sc_array_index_int (&info->sides, j);
+        qid1 = side1->quadid +
+          (side1->is_ghost ? mesh->local_num_quadrants : qoffset);
+        visited[j] = 1;
+        continue;
+      }
+
+      /* Examine a potential second side */
+      P4EST_ASSERT (side2 == NULL);
+      side2 =
+        (p4est_iter_corner_side_t *) sc_array_index_int (&info->sides, j);
+      P4EST_ASSERT (side1->treeid == side2->treeid);
+      if (side1->corner + side2->corner != P4EST_CHILDREN - 1) {
+        side2 = NULL;
+        continue;
+      }
+
+      /* We have found a diagonally opposite second side */
+      qid2 = side2->quadid +
+        (side2->is_ghost ? mesh->local_num_quadrants : qoffset);
+      if (!side1->is_ghost) {
+        P4EST_ASSERT (0 <= qid1 && qid1 < mesh->local_num_quadrants);
+        P4EST_ASSERT (mesh->quad_to_corner[P4EST_CHILDREN * qid1 +
+                                           side1->corner] == -1);
+        mesh->quad_to_corner[P4EST_CHILDREN * qid1 + side1->corner] = qid2;
+      }
+      if (!side2->is_ghost) {
+        P4EST_ASSERT (0 <= qid2 && qid2 < mesh->local_num_quadrants);
+        P4EST_ASSERT (mesh->quad_to_corner[P4EST_CHILDREN * qid2 +
+                                           side2->corner] == -1);
+        mesh->quad_to_corner[P4EST_CHILDREN * qid2 + side2->corner] = qid1;
+      }
+      visited[j] = 1;
+      break;
+    }
+    P4EST_ASSERT (side1 != NULL && side2 != NULL);
+  }
+}
+
+static void
+mesh_iter_face (p4est_iter_face_info_t * info, void *user_data)
+{
+  int                 h;
+  int                 swapsides;
+  p4est_mesh_t       *mesh = (p4est_mesh_t *) user_data;
+  p4est_locidx_t      jl, jl2, jls[P4EST_HALF];
+  p4est_locidx_t      in_qtoq, halfindex;
+  p4est_locidx_t     *halfentries;
+  p4est_tree_t       *tree;
+  p4est_iter_face_side_t *side, *side2, *tempside;
+
+  if (info->sides.elem_count == 1) {
+    /* this face is on an outside boundary of the forest */
+    P4EST_ASSERT (info->orientation == 0);
+    P4EST_ASSERT (info->tree_boundary);
+    side = (p4est_iter_face_side_t *) sc_array_index (&info->sides, 0);
+    P4EST_ASSERT (0 <= side->treeid &&
+                  side->treeid < info->p4est->connectivity->num_trees);
+    P4EST_ASSERT (0 <= side->face && side->face < P4EST_FACES);
+    P4EST_ASSERT (!side->is_hanging && !side->is.full.is_ghost);
+    tree = p4est_tree_array_index (info->p4est->trees, side->treeid);
+    jl = side->is.full.quadid + tree->quadrants_offset;
+    P4EST_ASSERT (0 <= jl && jl < mesh->local_num_quadrants);
+    in_qtoq = P4EST_FACES * jl + side->face;
+    mesh->quad_to_quad[in_qtoq] = jl;   /* put in myself and my own face */
+    mesh->quad_to_face[in_qtoq] = side->face;
+  }
+  else {
+    /* this face is between two quadrants */
+    P4EST_ASSERT (info->orientation == 0 || info->tree_boundary);
+    P4EST_ASSERT (info->sides.elem_count == 2);
+    side = (p4est_iter_face_side_t *) sc_array_index (&info->sides, 0);
+    side2 = (p4est_iter_face_side_t *) sc_array_index (&info->sides, 1);
+    P4EST_ASSERT (info->tree_boundary || side->treeid == side2->treeid);
+    P4EST_ASSERT (!side->is_hanging || !side2->is_hanging);
+    if (!side->is_hanging && !side2->is_hanging) {
+      /* same-size face neighbors */
+      P4EST_ASSERT (!side->is.full.is_ghost || !side2->is.full.is_ghost);
+
+      /* determine both quadrant numbers */
+      if (!side->is.full.is_ghost) {
+        tree = p4est_tree_array_index (info->p4est->trees, side->treeid);
+        jl = side->is.full.quadid + tree->quadrants_offset;
+        P4EST_ASSERT (0 <= jl && jl < mesh->local_num_quadrants);
+      }
+      else {
+        P4EST_ASSERT (side->is.full.quad != NULL);
+        P4EST_ASSERT (side->is.full.quadid >= 0);
+        jl = mesh->local_num_quadrants + side->is.full.quadid;
+      }
+      if (!side2->is.full.is_ghost) {
+        tree = p4est_tree_array_index (info->p4est->trees, side2->treeid);
+        jl2 = side2->is.full.quadid + tree->quadrants_offset;
+        P4EST_ASSERT (0 <= jl2 && jl2 < mesh->local_num_quadrants);
+      }
+      else {
+        P4EST_ASSERT (side2->is.full.quad != NULL);
+        P4EST_ASSERT (side2->is.full.quadid >= 0);
+        jl2 = mesh->local_num_quadrants + side2->is.full.quadid;
+      }
+
+      /* encode quadrant neighborhood */
+      if (!side->is.full.is_ghost) {
+        in_qtoq = P4EST_FACES * jl + side->face;
+        P4EST_ASSERT (mesh->quad_to_quad[in_qtoq] == -1);
+        P4EST_ASSERT (mesh->quad_to_face[in_qtoq] == -25);
+        mesh->quad_to_quad[in_qtoq] = jl2;
+        mesh->quad_to_face[in_qtoq] =
+          P4EST_FACES * info->orientation + side2->face;
+      }
+      if (!side2->is.full.is_ghost) {
+        in_qtoq = P4EST_FACES * jl2 + side2->face;
+        P4EST_ASSERT (mesh->quad_to_quad[in_qtoq] == -1);
+        P4EST_ASSERT (mesh->quad_to_face[in_qtoq] == -25);
+        mesh->quad_to_quad[in_qtoq] = jl;
+        mesh->quad_to_face[in_qtoq] =
+          P4EST_FACES * info->orientation + side->face;
+      }
+    }
+    else {
+      /* one of the faces is hanging, rename so it's always side2 */
+      swapsides = side->is_hanging;
+      if (swapsides) {
+        tempside = side;
+        side = side2;
+        side2 = tempside;
+      }
+      P4EST_ASSERT (!side->is_hanging && side2->is_hanging);
+
+      /* determine quadrant number for non-hanging large face */
+      if (!side->is.full.is_ghost) {
+        tree = p4est_tree_array_index (info->p4est->trees, side->treeid);
+        jl = side->is.full.quadid + tree->quadrants_offset;
+        P4EST_ASSERT (0 <= jl && jl < mesh->local_num_quadrants);
+      }
+      else {
+        P4EST_ASSERT (side->is.full.quad != NULL);
+        P4EST_ASSERT (side->is.full.quadid >= 0);
+        jl = mesh->local_num_quadrants + side->is.full.quadid;
+      }
+
+      /* determine quadrant numbers for all hanging faces */
+      for (h = 0; h < P4EST_HALF; ++h) {
+        if (!side2->is.hanging.is_ghost[h]) {
+          tree = p4est_tree_array_index (info->p4est->trees, side2->treeid);
+          jls[h] = side2->is.hanging.quadid[h] + tree->quadrants_offset;
+          P4EST_ASSERT (0 <= jls[h] && jls[h] < mesh->local_num_quadrants);
+        }
+        else {
+          P4EST_ASSERT (side2->is.hanging.quad[h] != NULL);
+          P4EST_ASSERT (side2->is.hanging.quadid[h] >= 0);
+          jls[h] = mesh->local_num_quadrants + side2->is.hanging.quadid[h];
+        }
+      }
+
+      /* encode quadrant neighborhood */
+      if (!side->is.full.is_ghost) {
+        in_qtoq = P4EST_FACES * jl + side->face;
+        P4EST_ASSERT (mesh->quad_to_quad[in_qtoq] == -1);
+        P4EST_ASSERT (mesh->quad_to_face[in_qtoq] == -25);
+        halfindex = (p4est_locidx_t) mesh->quad_to_half->elem_count;
+        mesh->quad_to_quad[in_qtoq] = halfindex;
+        mesh->quad_to_face[in_qtoq] =
+          P4EST_FACES * (info->orientation - P4EST_HALF) + side2->face;
+        halfentries = (p4est_locidx_t *) sc_array_push (mesh->quad_to_half);
+        for (h = 0; h < P4EST_HALF; ++h) {
+          halfentries[h] = jls[h];
+        }
+      }
+      for (h = 0; h < P4EST_HALF; ++h) {
+        if (!side2->is.hanging.is_ghost[h]) {
+          in_qtoq = P4EST_FACES * jls[h] + side2->face;
+          P4EST_ASSERT (mesh->quad_to_quad[in_qtoq] == -1);
+          P4EST_ASSERT (mesh->quad_to_face[in_qtoq] == -25);
+          mesh->quad_to_quad[in_qtoq] = jl;
+          mesh->quad_to_face[in_qtoq] =
+            P4EST_FACES * (info->orientation + (h + 1) * P4EST_HALF) +
+            side->face;
+        }
+      }
+    }
+  }
+}
+
+static void
+mesh_iter_volume (p4est_iter_volume_info_t * info, void *user_data)
+{
+  p4est_mesh_t       *mesh = (p4est_mesh_t *) user_data;
+  p4est_tree_t       *tree;
+  p4est_locidx_t     *quadid;
+  int                 level = info->quad->level;
+
+  /* We could use a static quadrant counter, but that gets uglier */
+  tree = p4est_tree_array_index (info->p4est->trees, info->treeid);
+  P4EST_ASSERT (0 <= info->quadid &&
+                info->quadid < (p4est_locidx_t) tree->quadrants.elem_count);
+
+  if (mesh->quad_to_tree != NULL) {
+    mesh->quad_to_tree[tree->quadrants_offset + info->quadid] = info->treeid;
+  }
+
+  if (mesh->quad_level != NULL) {
+    quadid = (p4est_locidx_t *) sc_array_push (mesh->quad_level + level);
+    *quadid = tree->quadrants_offset + info->quadid;
+  }
+}
+
+size_t
+p4est_mesh_memory_used (p4est_mesh_t * mesh)
+{
+  size_t              lqz, ngz;
+  int                 level;
+  size_t              qtt_memory = 0;
+  size_t              ql_memory = 0;
+  size_t              all_memory;
+
+  lqz = (size_t) mesh->local_num_quadrants;
+  ngz = (size_t) mesh->ghost_num_quadrants;
+
+  if (mesh->quad_to_tree != NULL) {
+    qtt_memory = sizeof (p4est_locidx_t) * lqz;
+  }
+
+  if (mesh->quad_level != NULL) {
+    ql_memory = sizeof (sc_array_t) * (P4EST_QMAXLEVEL + 1);
+    for (level = 0; level <= P4EST_QMAXLEVEL; ++level) {
+      ql_memory += sc_array_memory_used (mesh->quad_level + level, 0);
+    }
+  }
+
+  /* basic memory plus face information */
+  all_memory =
+    sizeof (p4est_mesh_t) + qtt_memory + ql_memory +
+    P4EST_FACES * lqz * (sizeof (p4est_locidx_t) + sizeof (int8_t)) +
+    ngz * sizeof (int) + sc_array_memory_used (mesh->quad_to_half, 1);
+
+  /* add corner information */
+  if (mesh->quad_to_corner != NULL) {
+    all_memory +=
+      P4EST_CHILDREN * lqz * sizeof (p4est_locidx_t) +
+      sc_array_memory_used (mesh->corner_offset, 1) +
+      sc_array_memory_used (mesh->corner_quad, 1) +
+      sc_array_memory_used (mesh->corner_corner, 1);
+  }
+
+  return all_memory;
+}
+
+p4est_mesh_t       *
+p4est_mesh_new (p4est_t * p4est, p4est_ghost_t * ghost,
+                p4est_connect_type_t btype)
+{
+  return p4est_mesh_new_ext (p4est, ghost, 0, 0, btype);
+}
+
+p4est_mesh_t       *
+p4est_mesh_new_ext (p4est_t * p4est, p4est_ghost_t * ghost,
+                    int compute_tree_index, int compute_level_lists,
+                    p4est_connect_type_t btype)
+{
+  int                 do_corner = 0;
+  int                 do_volume = 0;
+  int                 rank;
+  p4est_locidx_t      lq, ng;
+  p4est_locidx_t      jl;
+  p4est_mesh_t       *mesh;
+
+  P4EST_ASSERT (p4est_is_balanced (p4est, P4EST_CONNECT_FULL));
+
+  mesh = P4EST_ALLOC_ZERO (p4est_mesh_t, 1);
+
+  lq = mesh->local_num_quadrants = p4est->local_num_quadrants;
+  ng = mesh->ghost_num_quadrants = (p4est_locidx_t) ghost->ghosts.elem_count;
+
+  if (btype == P4EST_CONNECT_FULL) {
+    do_corner = 1;
+  }
+  do_volume = (compute_tree_index || compute_level_lists ? 1 : 0);
+
+  /* Optional map of tree index for each quadrant */
+  if (compute_tree_index) {
+    mesh->quad_to_tree = P4EST_ALLOC (p4est_topidx_t, lq);
+  }
+
+  mesh->ghost_to_proc = P4EST_ALLOC (int, ng);
+  mesh->quad_to_quad = P4EST_ALLOC (p4est_locidx_t, P4EST_FACES * lq);
+  mesh->quad_to_face = P4EST_ALLOC (int8_t, P4EST_FACES * lq);
+  mesh->quad_to_half = sc_array_new (P4EST_HALF * sizeof (p4est_locidx_t));
+
+  /* Optional per-level lists of quadrants */
+  if (compute_level_lists) {
+    mesh->quad_level = P4EST_ALLOC (sc_array_t, P4EST_QMAXLEVEL + 1);
+
+    for (jl = 0; jl <= P4EST_QMAXLEVEL; ++jl) {
+      sc_array_init (mesh->quad_level + jl, sizeof (p4est_locidx_t));
+    }
+  }
+
+  /* Populate ghost information */
+  rank = 0;
+  for (jl = 0; jl < ng; ++jl) {
+    while (ghost->proc_offsets[rank + 1] <= jl) {
+      ++rank;
+      P4EST_ASSERT (rank < p4est->mpisize);
+    }
+    mesh->ghost_to_proc[jl] = rank;
+  }
+
+  /* Fill face arrays with default values */
+  memset (mesh->quad_to_quad, -1, P4EST_FACES * lq * sizeof (p4est_locidx_t));
+  memset (mesh->quad_to_face, -25, P4EST_FACES * lq * sizeof (int8_t));
+
+  if (do_corner) {
+    /* Initialize corner information to a consistent state */
+    mesh->quad_to_corner = P4EST_ALLOC (p4est_locidx_t, P4EST_CHILDREN * lq);
+    memset (mesh->quad_to_corner, -1, P4EST_CHILDREN * lq *
+            sizeof (p4est_locidx_t));
+
+    mesh->corner_offset = sc_array_new (sizeof (p4est_locidx_t));
+    *(p4est_locidx_t *) sc_array_push (mesh->corner_offset) = 0;
+
+    mesh->corner_quad = sc_array_new (sizeof (p4est_locidx_t));
+    mesh->corner_corner = sc_array_new (sizeof (int8_t));
+  }
+
+  /* Call the forest iterator to collect face connectivity */
+  p4est_iterate (p4est, ghost, mesh,
+                 (do_volume ? mesh_iter_volume : NULL), mesh_iter_face,
+#ifdef P4_TO_P8
+                 NULL,
+#endif
+                 do_corner ? mesh_iter_corner : NULL);
+
+  return mesh;
+}
+
+void
+p4est_mesh_destroy (p4est_mesh_t * mesh)
+{
+  int                 level = 0;
+
+  if (mesh->quad_to_tree != NULL) {
+    P4EST_FREE (mesh->quad_to_tree);
+  }
+
+  if (mesh->quad_level != NULL) {
+    for (level = 0; level <= P4EST_QMAXLEVEL; ++level) {
+      sc_array_reset (mesh->quad_level + level);
+    }
+    P4EST_FREE (mesh->quad_level);
+  }
+
+  P4EST_FREE (mesh->ghost_to_proc);
+  P4EST_FREE (mesh->quad_to_quad);
+  P4EST_FREE (mesh->quad_to_face);
+  sc_array_destroy (mesh->quad_to_half);
+
+  if (mesh->quad_to_corner != NULL) {
+    P4EST_FREE (mesh->quad_to_corner);
+    sc_array_destroy (mesh->corner_offset);
+    sc_array_destroy (mesh->corner_quad);
+    sc_array_destroy (mesh->corner_corner);
+  }
+
+  P4EST_FREE (mesh);
+}
+
+p4est_quadrant_t   *
+p4est_mesh_quadrant_cumulative (p4est_t * p4est, p4est_locidx_t cumulative_id,
+                                p4est_topidx_t * which_tree,
+                                p4est_locidx_t * quadrant_id)
+{
+  int                 the_quadrant_id;
+  p4est_topidx_t      low_tree, high_tree, guess_tree;
+  p4est_tree_t       *tree;
+
+  P4EST_ASSERT (0 <= cumulative_id &&
+                cumulative_id < p4est->local_num_quadrants);
+
+  low_tree = p4est->first_local_tree;
+  high_tree = p4est->last_local_tree;
+  if (which_tree != NULL && *which_tree != -1) {
+    guess_tree = *which_tree;
+  }
+  else {
+    guess_tree = (low_tree + high_tree) / 2;
+  }
+  for (;;) {
+    P4EST_ASSERT (p4est->first_local_tree <= low_tree);
+    P4EST_ASSERT (high_tree <= p4est->last_local_tree);
+    P4EST_ASSERT (low_tree <= guess_tree && guess_tree <= high_tree);
+
+    tree = p4est_tree_array_index (p4est->trees, guess_tree);
+    if (cumulative_id < tree->quadrants_offset) {
+      high_tree = guess_tree - 1;
+    }
+    else if (cumulative_id >= tree->quadrants_offset +
+             (p4est_locidx_t) tree->quadrants.elem_count) {
+      low_tree = guess_tree + 1;
+    }
+    else {
+      the_quadrant_id = cumulative_id - tree->quadrants_offset;
+      P4EST_ASSERT (0 <= the_quadrant_id);
+
+      if (which_tree != NULL) {
+        *which_tree = guess_tree;
+      }
+      if (quadrant_id != NULL) {
+        *quadrant_id = the_quadrant_id;
+      }
+      return p4est_quadrant_array_index (&tree->quadrants,
+                                         (size_t) the_quadrant_id);
+    }
+    guess_tree = (low_tree + high_tree) / 2;
+  }
+}
+
+void
+p4est_mesh_face_neighbor_init2 (p4est_mesh_face_neighbor_t * mfn,
+                                p4est_t * p4est, p4est_ghost_t * ghost,
+                                p4est_mesh_t * mesh,
+                                p4est_topidx_t which_tree,
+                                p4est_locidx_t quadrant_id)
+{
+  p4est_tree_t       *tree;
+
+  mfn->p4est = p4est;
+  mfn->ghost = ghost;
+  mfn->mesh = mesh;
+
+  P4EST_ASSERT (0 <= which_tree &&
+                which_tree < p4est->connectivity->num_trees);
+  mfn->which_tree = which_tree;
+  tree = p4est_tree_array_index (p4est->trees, which_tree);
+
+  P4EST_ASSERT (0 <= quadrant_id &&
+                (size_t) quadrant_id < tree->quadrants.elem_count);
+  mfn->quadrant_id = quadrant_id;
+  mfn->quadrant_code = P4EST_FACES * (tree->quadrants_offset + quadrant_id);
+
+  mfn->face = 0;
+  mfn->subface = 0;
+  mfn->current_qtq = -1;
+}
+
+void
+p4est_mesh_face_neighbor_init (p4est_mesh_face_neighbor_t * mfn,
+                               p4est_t * p4est, p4est_ghost_t * ghost,
+                               p4est_mesh_t * mesh, p4est_topidx_t which_tree,
+                               p4est_quadrant_t * quadrant)
+{
+  p4est_locidx_t      quadrant_id;
+  p4est_tree_t       *tree;
+
+  mfn->p4est = p4est;
+  mfn->ghost = ghost;
+  mfn->mesh = mesh;
+
+  P4EST_ASSERT (0 <= which_tree &&
+                which_tree < p4est->connectivity->num_trees);
+  mfn->which_tree = which_tree;
+  tree = p4est_tree_array_index (p4est->trees, which_tree);
+
+  quadrant_id =
+    (p4est_locidx_t) sc_array_position (&tree->quadrants, quadrant);
+  mfn->quadrant_id = quadrant_id;
+  mfn->quadrant_code = P4EST_FACES * (tree->quadrants_offset + quadrant_id);
+
+  mfn->face = 0;
+  mfn->subface = 0;
+  mfn->current_qtq = -1;
+}
+
+p4est_quadrant_t   *
+p4est_mesh_face_neighbor_next (p4est_mesh_face_neighbor_t * mfn,
+                               p4est_topidx_t * ntree, p4est_locidx_t * nquad,
+                               int *nface, int *nrank)
+{
+  int                 qtf;
+  p4est_topidx_t      which_tree;
+  p4est_locidx_t      qtq, quadfacecode;
+  p4est_locidx_t      lnq, *halfs;
+#ifdef P4EST_ENABLE_DEBUG
+  p4est_locidx_t      ngh;
+#endif
+  p4est_quadrant_t   *q;
+
+  /* We have already processed the last quadrant */
+  if (mfn->face == P4EST_FACES) {
+    mfn->current_qtq = -1;
+    P4EST_ASSERT (mfn->subface == 0);
+    return NULL;
+  }
+
+  /* Make sure we have a valid quadrant face and iterator */
+  lnq = mfn->mesh->local_num_quadrants;
+#ifdef P4EST_ENABLE_DEBUG
+  ngh = mfn->mesh->ghost_num_quadrants;
+#endif
+  P4EST_ASSERT (mfn->face >= 0 && mfn->face < P4EST_FACES);
+  P4EST_ASSERT (mfn->subface >= 0 && mfn->subface < P4EST_HALF);
+  P4EST_ASSERT (mfn->p4est->local_num_quadrants == lnq);
+  P4EST_ASSERT (mfn->ghost->ghosts.elem_count == (size_t) ngh);
+
+  /* Retrieve face and quadrant codes */
+  quadfacecode = mfn->quadrant_code + (p4est_locidx_t) mfn->face;
+  qtq = mfn->mesh->quad_to_quad[quadfacecode];
+  qtf = (int) mfn->mesh->quad_to_face[quadfacecode];
+  if (qtf >= 0) {
+    /* Neighbor is same or double size */
+    ;
+
+    /* Advance to next quadrant */
+    ++mfn->face;
+  }
+  else {
+    /* Neighbors across this face are half size */
+    P4EST_ASSERT (qtq >= 0);
+    halfs = (p4est_locidx_t *) sc_array_index (mfn->mesh->quad_to_half,
+                                               (size_t) qtq);
+    qtq = halfs[mfn->subface];
+
+    /* Advance to next quadrant */
+    if (++mfn->subface == P4EST_HALF) {
+      mfn->subface = 0;
+      ++mfn->face;
+    }
+  }
+
+  mfn->current_qtq = qtq;
+  /* From here on face and subface have advanced and can no longer be used */
+  P4EST_ASSERT (qtq >= 0);
+  if (qtq < lnq) {
+    /* Local quadrant */
+    which_tree = mfn->which_tree;
+    q = p4est_mesh_quadrant_cumulative (mfn->p4est, qtq, &which_tree, nquad);
+    if (ntree != NULL) {
+      *ntree = which_tree;
+    }
+    if (nrank != NULL) {
+      *nrank = mfn->p4est->mpirank;
+    }
+  }
+  else {
+    /* Ghost quadrant */
+    qtq -= lnq;
+    P4EST_ASSERT (qtq < ngh);
+    q = p4est_quadrant_array_index (&mfn->ghost->ghosts, (size_t) qtq);
+    if (ntree != NULL) {
+      *ntree = q->p.piggy3.which_tree;
+    }
+    if (nquad != NULL) {
+      *nquad = qtq;             /* number of ghost in the ghost layer */
+    }
+    if (nrank != NULL) {
+      *nrank = mfn->mesh->ghost_to_proc[qtq];
+    }
+  }
+  if (nface != NULL) {
+    *nface = qtf;
+  }
+
+  return q;
+}
+
+void               *
+p4est_mesh_face_neighbor_data (p4est_mesh_face_neighbor_t * mfn,
+                               void *ghost_data)
+{
+  p4est_locidx_t      qtq = mfn->current_qtq;
+  p4est_locidx_t      lnq = mfn->mesh->local_num_quadrants;
+  size_t              data_size = mfn->p4est->data_size;
+
+  P4EST_ASSERT (qtq >= 0);
+
+  if (qtq < lnq) {
+    p4est_topidx_t      which_tree;
+    p4est_quadrant_t   *q;
+    /* Local quadrant */
+    which_tree = mfn->which_tree;
+    q = p4est_mesh_quadrant_cumulative (mfn->p4est, qtq, &which_tree, NULL);
+    return q->p.user_data;
+  }
+  else {
+    qtq -= lnq;
+    return (void *) ((char *) ghost_data + data_size * qtq);
+  }
+}
diff --git a/src/p4est_mesh.h b/src/p4est_mesh.h
new file mode 100644
index 0000000..9ed53c5
--- /dev/null
+++ b/src/p4est_mesh.h
@@ -0,0 +1,228 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_mesh.h
+ *
+ * forest topology in a conventional mesh format
+ *
+ * \ingroup p4est
+ */
+
+#ifndef P4EST_MESH_H
+#define P4EST_MESH_H
+
+#include <p4est_ghost.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** This structure contains complete mesh information on the forest.
+ * It stores the locally relevant neighborhood, that is, all locally owned
+ * quadrants and one layer of adjacent ghost quadrants and their owners.
+ *
+ * For each local quadrant, its tree number is stored in quad_to_tree. The
+ * quad_to_tree array is NULL by default and can be enabled using
+ * p4est_mesh_new_ext.
+ * For each ghost quadrant, its owner rank is stored in ghost_to_proc.
+ * For each level, an array of local quadrant numbers is stored in quad_level.
+ * The quad_level array is NULL by default and can be enabled using
+ * p4est_mesh_new_ext.
+ *
+ * The quad_to_quad list stores one value for each local quadrant's face.
+ * This value is in 0..local_num_quadrants-1 for local quadrants, or in
+ * local_num_quadrants + (0..ghost_num_quadrants-1) for ghost quadrants.
+ * The quad_to_face list has equally many entries which are either:
+ * 1. A value of v = 0..7 indicates one same-size neighbor.
+ *    This value is decoded as v = r * 4 + nf, where nf = 0..3 is the
+ *    neighbor's connecting face number and r = 0..1 is the relative
+ *    orientation of the neighbor's face, see p4est_connectivity.h.
+ * 2. A value of v = 8..23 indicates a double-size neighbor.
+ *    This value is decoded as v = 8 + h * 8 + r * 4 + nf, where
+ *    r and nf are as above and h = 0..1 is the number of the subface.
+ * 3. A value of v = -8..-1 indicates two half-size neighbors.
+ *    In this case the corresponding quad_to_quad index points into the
+ *    quad_to_half array which stores two quadrant numbers per index,
+ *    and the orientation of the smaller faces follows from 8 + v.
+ *    The entries of quad_to_half encode between local and ghost quadrant
+ *    in the same way as the quad_to_quad values described above.
+ * A quadrant on the boundary of the forest sees itself and its face number.
+ *
+ * The quad_to_corner list stores corner neighbors that are not face neighbors.
+ * On the inside of a tree, there is precisely one such neighbor per corner.
+ * In this case, its index is encoded as described above for quad_to_quad.
+ * The neighbor's matching corner number is always diagonally opposite.
+ *
+ * On the inside of an inter-tree face, we have precisely one corner neighbor.
+ * If a corner is an inter-tree corner, then the number of corner neighbors
+ * may be any non-negative number.  In both cases, the quad_to_corner value
+ * is in
+ *    local_num_quadrants + local_num_ghosts + [0 .. local_num_corners - 1]
+ * where the offset by local quadrants and ghosts is implicitly substracted.
+ * It indexes into corner_offset, which encodes a group of corner neighbors.
+ * Each group contains the quadrant numbers encoded as usual for quad_to_quad
+ * in corner_quad, and the corner number from the neighbor as corner_corner.
+ *
+ * Intra-tree corners and corners across an inter-tree face are implemented.
+ * Other Inter-tree corners are NOT IMPLEMENTED and are assigned the value -2.
+ * Corners with no diagonal neighbor at all are assigned the value -1.
+ */
+typedef struct
+{
+  p4est_locidx_t      local_num_quadrants;
+  p4est_locidx_t      ghost_num_quadrants;
+
+  p4est_topidx_t     *quad_to_tree;     /**< tree index for each local quad,
+                                             NULL by default */
+  int                *ghost_to_proc;    /**< processor for each ghost quad */
+
+  p4est_locidx_t     *quad_to_quad;     /**< one index for each of the 4 faces */
+  int8_t             *quad_to_face;     /**< encodes orientation/2:1 status */
+  sc_array_t         *quad_to_half;     /**< stores half-size neighbors */
+  sc_array_t         *quad_level;       /**< stores lists of per-level quads,
+                                             NULL by default */
+
+  /* These members are NULL if the connect_t is not P4EST_CONNECT_CORNER */
+  /* CAUTION: tree-boundary corners not yet implemented */
+  p4est_locidx_t      local_num_corners;        /* tree-boundary corners */
+  p4est_locidx_t     *quad_to_corner;   /* 4 indices for each local quad */
+  sc_array_t         *corner_offset;    /* local_num_corners + 1 entries */
+  sc_array_t         *corner_quad;      /* corner_offset indexes into this */
+  sc_array_t         *corner_corner;    /* and this one too (type int8_t) */
+}
+p4est_mesh_t;
+
+/** This structure can be used as the status of a face neighbor iterator.
+  * It always contains the face and subface of the neighbor to be processed.
+  */
+typedef struct
+{
+  /* forest information */
+  p4est_t            *p4est;
+  p4est_ghost_t      *ghost;
+  p4est_mesh_t       *mesh;
+
+  /* quadrant information */
+  p4est_topidx_t      which_tree;
+  p4est_locidx_t      quadrant_id;      /* tree-local quadrant index */
+  p4est_locidx_t      quadrant_code;    /* 4 * (quadrant_id + tree_offset) */
+
+  /* neighbor information */
+  int                 face;     /* Face number in 0..3. */
+  int                 subface;  /* Hanging neighbor number in 0..1. */
+
+  /* internal information */
+  p4est_locidx_t      current_qtq;
+}
+p4est_mesh_face_neighbor_t;
+
+/** Calculate the memory usage of the mesh structure.
+ * \param [in] mesh     Mesh structure.
+ * \return              Memory used in bytes.
+ */
+size_t              p4est_mesh_memory_used (p4est_mesh_t * mesh);
+
+/** Create a p4est_mesh structure.
+ * \param [in] p4est    A forest that is fully 2:1 balanced.
+ * \param [in] ghost    The ghost layer created from the provided p4est.
+ * \param [in] btype    Determines the highest codimension of neighbors.
+ * \return              A fully allocated mesh structure.
+ */
+p4est_mesh_t       *p4est_mesh_new (p4est_t * p4est, p4est_ghost_t * ghost,
+                                    p4est_connect_type_t btype);
+
+/** Destroy a p4est_mesh structure.
+ * \param [in] mesh     Mesh structure previously created by p4est_mesh_new.
+ */
+void                p4est_mesh_destroy (p4est_mesh_t * mesh);
+
+/** Find a quadrant based on its cumulative number in the local forest.
+ * \param [in]  p4est           Forest to be worked with.
+ * \param [in]  cumulative_id   Cumulative index over all trees of quadrant.
+ * \param [in,out] which_tree   If not NULL, the input value can be -1
+ *                              or an initial guess for the quadrant's tree
+ *                              and output is the tree of returned quadrant.
+ * \param [out] quadrant_id     If not NULL, the number of quadrant in tree.
+ * \return                      The identified quadrant.
+ */
+p4est_quadrant_t   *p4est_mesh_quadrant_cumulative (p4est_t * p4est,
+                                                    p4est_locidx_t
+                                                    cumulative_id,
+                                                    p4est_topidx_t
+                                                    * which_tree,
+                                                    p4est_locidx_t
+                                                    * quadrant_id);
+
+/** Initialize a mesh neighbor iterator by quadrant index.
+ * \param [out] mfn         A p4est_mesh_face_neighbor_t to be initialized.
+ * \param [in]  which_tree  Tree of quadrant whose neighbors are looped over.
+ * \param [in]  quadrant_id Index relative to which_tree of quadrant.
+ */
+void                p4est_mesh_face_neighbor_init2 (p4est_mesh_face_neighbor_t
+                                                    * mfn, p4est_t * p4est,
+                                                    p4est_ghost_t * ghost,
+                                                    p4est_mesh_t * mesh,
+                                                    p4est_topidx_t which_tree,
+                                                    p4est_locidx_t
+                                                    quadrant_id);
+
+/** Initialize a mesh neighbor iterator by quadrant pointer.
+ * \param [out] mfn         A p4est_mesh_face_neighbor_t to be initialized.
+ * \param [in]  which_tree  Tree of quadrant whose neighbors are looped over.
+ * \param [in]  quadrant    Pointer to quadrant contained in which_tree.
+ */
+void                p4est_mesh_face_neighbor_init (p4est_mesh_face_neighbor_t
+                                                   * mfn, p4est_t * p4est,
+                                                   p4est_ghost_t * ghost,
+                                                   p4est_mesh_t * mesh,
+                                                   p4est_topidx_t which_tree,
+                                                   p4est_quadrant_t
+                                                   * quadrant);
+
+/** Move the iterator forward to loop around neighbors of the quadrant.
+ * \param [in,out] mfn      Internal status of the iterator.
+ * \param [out]    ntree    If not NULL, the tree number of the neighbor.
+ * \param [out]    nquad    If not NULL, the quadrant number within tree.
+ *                          For ghosts instead the number in ghost layer.
+ * \param [out]    nface    If not NULL, neighbor's face as in p4est_mesh_t.
+ * \param [out]    nrank    If not NULL, the owner process of the neighbor.
+ * \return                  Either a real quadrant or one from the ghost layer.
+ *                          Returns NULL when the iterator is done.
+ */
+p4est_quadrant_t   *p4est_mesh_face_neighbor_next (p4est_mesh_face_neighbor_t
+                                                   * mfn,
+                                                   p4est_topidx_t * ntree,
+                                                   p4est_locidx_t * nquad,
+                                                   int *nface, int *nrank);
+
+/** Get the user data for the current face neighbor.
+ * \param [in]     mfn           Internal status of the iterator.
+ * \param [in]     ghost_data    Data for the ghost quadrants that has been
+ *                               synchronized with p4est_ghost_exchange_data.
+ * \return                       A pointer to the user data for the current
+ *                               neighbor.
+ */
+void               *p4est_mesh_face_neighbor_data (p4est_mesh_face_neighbor_t
+                                                   * mfn, void *ghost_data);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_MESH_H */
diff --git a/src/p4est_nodes.c b/src/p4est_nodes.c
new file mode 100644
index 0000000..b2a5d4e
--- /dev/null
+++ b/src/p4est_nodes.c
@@ -0,0 +1,1526 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_communication.h>
+#include <p4est_nodes.h>
+#else
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_communication.h>
+#include <p8est_nodes.h>
+#endif
+#include <sc_ranges.h>
+
+#ifdef P4EST_ENABLE_MPI
+
+#define p4est_num_ranges (25)
+
+typedef struct
+{
+  int                 expect_query, expect_reply;
+  size_t              recv_offset;
+  sc_array_t          send_first, send_second;
+  sc_array_t          recv_first, recv_second;
+}
+p4est_node_peer_t;
+
+#endif
+
+/** Generate a neighbor of a quadrant for a given quadrant corner.
+ * \param [in]  q             quadrant whose possible corner neighbor is built.
+ * \param [in]  corner        the corner of the quadrant \a q whose possible
+ *                            corner neighbor list will be indexed.
+ * \param [in]  nnum          neighbor number around the specified corner.
+ *                            This is also the number of the neighbor's corner
+ *                            that is shared with \a q.
+ *                            The case nnum==corner is forbidden.
+ * \param [in]  neighor_rlev  the relative level of the neighbor compared to
+ *                            the level of \a q, in -1..1.
+ * \param [out] neighbor      the neighbor that will be filled.
+ */
+static void
+p4est_possible_corner_neighbor (const p4est_quadrant_t * q, int corner,
+                                int nnum, int neighbor_rlev,
+                                p4est_quadrant_t * n)
+{
+  const int           nlevel = (int) q->level + neighbor_rlev;
+  const p4est_qcoord_t qh = P4EST_QUADRANT_LEN (q->level);
+  const p4est_qcoord_t nh = P4EST_QUADRANT_LEN (nlevel);
+#ifdef P4EST_ENABLE_DEBUG
+  int                 qcid;
+#endif
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+  P4EST_ASSERT (-1 <= neighbor_rlev && neighbor_rlev <= 1);
+  P4EST_ASSERT (0 <= nlevel && nlevel <= P4EST_QMAXLEVEL);
+  P4EST_ASSERT (corner != nnum);
+
+#ifdef P4EST_ENABLE_DEBUG
+  /* Check to see if it is possible to construct the neighbor */
+  qcid = p4est_quadrant_child_id (q);
+  P4EST_ASSERT (neighbor_rlev >= 0 || qcid == corner);
+#endif
+
+  P4EST_QUADRANT_INIT (n);
+  n->x = q->x + (corner & 0x01) * qh - (nnum & 0x01) * nh;
+  n->y = q->y + ((corner & 0x02) >> 1) * qh - ((nnum & 0x02) >> 1) * nh;
+#ifdef P4_TO_P8
+  n->z = q->z + ((corner & 0x04) >> 2) * qh - ((nnum & 0x04) >> 2) * nh;
+#endif
+  n->level = (int8_t) nlevel;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (n));
+}
+
+static p4est_nodes_t *
+p4est_nodes_new_local (p4est_t * p4est)
+{
+  const int           rank = p4est->mpirank;
+  int                 qcid;
+  int                 corner, nnum, rlev;
+  int                 neighbor_proc;
+#ifdef P4EST_ENABLE_DEBUG
+  int                 is_balanced;
+#endif
+  p4est_topidx_t      first_local_tree = p4est->first_local_tree;
+  p4est_topidx_t      last_local_tree = p4est->last_local_tree;
+  p4est_topidx_t      jt;
+  p4est_locidx_t      il, Ncells = p4est->local_num_quadrants;
+  p4est_locidx_t      vertex_num;
+  p4est_locidx_t      lqid;
+  p4est_locidx_t      tree_offset;
+  p4est_locidx_t     *ln;
+  size_t              zz, numz_quadrants;
+  ssize_t             lnid;
+  p4est_tree_t       *tree;
+  sc_array_t         *quadrants;
+  p4est_quadrant_t    neighbor;
+  p4est_quadrant_t   *q;
+  p4est_indep_t      *in;
+  p4est_nodes_t      *nodes;
+
+  P4EST_GLOBAL_PRODUCTION ("Into " P4EST_STRING "_nodes_new_local\n");
+  p4est_log_indent_push ();
+  P4EST_ASSERT (p4est_is_valid (p4est));
+#ifdef P4EST_ENABLE_DEBUG
+  is_balanced = p4est_is_balanced (p4est, P4EST_CONNECT_FULL);
+#endif
+
+  P4EST_QUADRANT_INIT (&neighbor);
+
+  nodes = P4EST_ALLOC (p4est_nodes_t, 1);
+  nodes->num_local_quadrants = Ncells;
+  nodes->num_owned_indeps = -1;
+  nodes->num_owned_shared = 0;
+  nodes->offset_owned_indeps = 0;
+  sc_array_init (&nodes->indep_nodes, sizeof (p4est_indep_t));
+#ifndef P4_TO_P8
+  sc_array_init (&nodes->face_hangings, sizeof (p4est_hang2_t));
+#else
+  sc_array_init (&nodes->face_hangings, sizeof (p8est_hang4_t));
+  sc_array_init (&nodes->edge_hangings, sizeof (p8est_hang2_t));
+#endif
+  nodes->local_nodes = P4EST_ALLOC (p4est_locidx_t, P4EST_CHILDREN * Ncells);
+  sc_array_init (&nodes->shared_indeps, sizeof (sc_recycle_array_t));
+  nodes->shared_offsets = NULL;
+  nodes->nonlocal_ranks = NULL;
+  nodes->global_owned_indeps = NULL;
+
+  /* Initialize vertex list to all -1.  Legitimate values are >= 0.  */
+  ln = nodes->local_nodes;
+  for (il = 0; il < P4EST_CHILDREN * Ncells; ++il) {
+    ln[il] = -1;
+  }
+
+  /* Loop over all local trees to generate the connectivity list */
+  ln = nodes->local_nodes;
+  for (jt = first_local_tree, vertex_num = 0, lqid = 0, tree_offset = 0;
+       jt <= last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    quadrants = &tree->quadrants;
+    numz_quadrants = quadrants->elem_count;
+
+    /* Find the neighbors of each quadrant */
+    for (zz = 0; zz < numz_quadrants; ++zz, ++lqid) {
+      q = p4est_quadrant_array_index (quadrants, zz);
+
+      /* loop over the corners of the quadrant */
+      for (corner = 0; corner < P4EST_CHILDREN; ++corner) {
+
+        /* Check to see if we have a new vertex */
+        if (ln[lqid * P4EST_CHILDREN + corner] == -1) {
+          ln[lqid * P4EST_CHILDREN + corner] = vertex_num;
+          in = (p4est_indep_t *) sc_array_push (&nodes->indep_nodes);
+          p4est_quadrant_corner_node (q, corner, (p4est_quadrant_t *) in);
+          in->pad8 = 0;
+          in->pad16 = 0;
+          in->p.piggy3.which_tree = jt;
+          in->p.piggy3.local_num = vertex_num;
+
+          /* loop over the possible corner neighbors to match new vertex */
+          for (nnum = 0; nnum < P4EST_CHILDREN; ++nnum) {
+            /* Don't search for the quadrant q itself */
+            if (nnum == corner)
+              continue;
+
+            qcid = p4est_quadrant_child_id (q);
+
+            /* loop over possible neighbor sizes */
+            for (rlev = -1; rlev < 2; ++rlev) {
+              /* can't check for quadrants larger than the root */
+              if (q->level == 0 && rlev < 0)
+                continue;
+              /* can't check for quadrants larger unless child id
+               * and corner line up
+               */
+              if (qcid != corner && rlev < 0)
+                continue;
+
+              /* get possible neighbor */
+              p4est_possible_corner_neighbor (q, corner, nnum, rlev,
+                                              &neighbor);
+
+              if (p4est_quadrant_is_inside_root (&neighbor)) {
+                /* neighbor is in the same tree */
+
+                neighbor_proc = p4est_comm_find_owner (p4est, jt, &neighbor,
+                                                       rank);
+
+                /* Neighbor is remote so we don't number its node */
+                if (neighbor_proc != rank)
+                  continue;
+
+                lnid = sc_array_bsearch (quadrants, &neighbor,
+                                         p4est_quadrant_compare);
+                if (lnid != -1) {
+                  lnid += tree_offset;
+                  /* We have found a neighbor in the same tree */
+                  if (ln[lnid * P4EST_CHILDREN + nnum] == -1) {
+                    /* This branch an invariant for corner-balanced forest */
+                    ln[lnid * P4EST_CHILDREN + nnum] = vertex_num;
+                  }
+                  else {
+                    /* This can only happen if not corner-balanced */
+                    P4EST_ASSERT (!is_balanced);
+                  }
+                  /* No need to check for more quadrants for this neighbor */
+                  break;
+                }
+              }
+            }
+          }
+          ++vertex_num;
+        }
+      }
+    }
+    tree_offset += (p4est_locidx_t) numz_quadrants;
+  }
+
+  nodes->num_owned_indeps = vertex_num;
+
+  P4EST_ASSERT (tree_offset == Ncells);
+  P4EST_ASSERT (lqid == Ncells);
+  P4EST_ASSERT ((size_t) vertex_num == nodes->indep_nodes.elem_count);
+  P4EST_ASSERT (p4est_nodes_is_valid (p4est, nodes));
+
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_nodes_new_local\n");
+
+  return nodes;
+}
+
+/** Determine the owning tree for a node and clamp it inside the domain.
+ *
+ * If the node is on the boundary, assign the lowest tree to own it.
+ * Clamp it inside the tree bounds if necessary.
+ *
+ * \param [in] p4est    The p4est to work on.
+ * \param [in] treeid   Original tree index for this node.
+ * \param [in] n        The node to work on.
+ * \param [out] c       The clamped node in owning tree coordinates.
+ *                      Its piggy data will be filled with owning tree id.
+ */
+static void
+p4est_node_canonicalize (p4est_t * p4est, p4est_topidx_t treeid,
+                         const p4est_quadrant_t * n, p4est_quadrant_t * c)
+{
+  p4est_connectivity_t *conn = p4est->connectivity;
+  int                 face_axis[3];     /* 3 not P4EST_DIM */
+  int                 quad_contact[P4EST_FACES];
+  int                 contacts, face, corner;
+  int                 ftransform[P4EST_FTRANSFORM];
+  size_t              ctreez;
+  p4est_topidx_t      ntreeid, ntreeid2, lowest;
+  p4est_quadrant_t    tmpq, o;
+#ifdef P4_TO_P8
+  int                 edge;
+  size_t              etreez;
+  p8est_edge_info_t   ei;
+  p8est_edge_transform_t *et;
+  sc_array_t         *eta;
+#endif
+  p4est_corner_info_t ci;
+  p4est_corner_transform_t *ct;
+  sc_array_t         *cta;
+
+  P4EST_ASSERT (treeid >= 0 && treeid < conn->num_trees);
+  P4EST_ASSERT (p4est_quadrant_is_node (n, 0));
+
+  P4EST_QUADRANT_INIT (&tmpq);
+  P4EST_QUADRANT_INIT (&o);
+
+  lowest = treeid;
+  p4est_node_clamp_inside (n, c);
+  c->p.which_tree = -1;
+
+  /* Check if the quadrant is inside the tree */
+  quad_contact[0] = (n->x == 0);
+  quad_contact[1] = (n->x == P4EST_ROOT_LEN);
+  face_axis[0] = quad_contact[0] || quad_contact[1];
+  quad_contact[2] = (n->y == 0);
+  quad_contact[3] = (n->y == P4EST_ROOT_LEN);
+  face_axis[1] = quad_contact[2] || quad_contact[3];
+#ifndef P4_TO_P8
+  face_axis[2] = 0;
+#else
+  quad_contact[4] = (n->z == 0);
+  quad_contact[5] = (n->z == P4EST_ROOT_LEN);
+  face_axis[2] = quad_contact[4] || quad_contact[5];
+#endif
+  contacts = face_axis[0] + face_axis[1] + face_axis[2];
+  if (contacts == 0) {
+    goto endfunction;
+  }
+
+  /* Check face neighbors */
+#ifdef P4EST_ENABLE_DEBUG
+  ntreeid = -1;
+#endif
+  for (face = 0; face < P4EST_FACES; ++face) {
+    if (!quad_contact[face]) {
+      /* The node is not touching this face */
+      continue;
+    }
+    ntreeid = conn->tree_to_tree[P4EST_FACES * treeid + face];
+    if (ntreeid == treeid
+        && ((int) conn->tree_to_face[P4EST_FACES * treeid + face] == face)) {
+      /* The node touches a face with no neighbor */
+      continue;
+    }
+    if (ntreeid > lowest) {
+      /* This neighbor tree is higher, so we keep the ownership */
+      continue;
+    }
+    /* Transform the node into the other tree's coordinates */
+    ntreeid2 = p4est_find_face_transform (conn, treeid, face, ftransform);
+    P4EST_ASSERT (ntreeid2 == ntreeid);
+    p4est_quadrant_transform_face (n, &o, ftransform);
+    if (ntreeid < lowest) {
+      /* we have found a new owning tree */
+      p4est_node_clamp_inside (&o, c);
+      lowest = ntreeid;
+    }
+    else {
+      P4EST_ASSERT (lowest == ntreeid);
+      p4est_node_clamp_inside (&o, &tmpq);
+      if (p4est_quadrant_compare (&tmpq, c) < 0) {
+        /* same tree (periodic) and the new position is lower than the old */
+        *c = tmpq;
+      }
+    }
+  }
+  P4EST_ASSERT (ntreeid >= 0);
+  if (contacts == 1) {
+    goto endfunction;
+  }
+
+#ifdef P4_TO_P8
+  P4EST_ASSERT (contacts >= 2);
+  eta = &ei.edge_transforms;
+  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+  for (edge = 0; edge < P8EST_EDGES; ++edge) {
+    if (!(quad_contact[p8est_edge_faces[edge][0]] &&
+          quad_contact[p8est_edge_faces[edge][1]])) {
+      continue;
+    }
+    p8est_find_edge_transform (conn, treeid, edge, &ei);
+    for (etreez = 0; etreez < eta->elem_count; ++etreez) {
+      et = p8est_edge_array_index (eta, etreez);
+      ntreeid = et->ntree;
+      if (ntreeid > lowest) {
+        /* This neighbor tree is higher, so we keep the ownership */
+        continue;
+      }
+      p8est_quadrant_transform_edge (n, &o, &ei, et, 0);
+      if (ntreeid < lowest) {
+        p4est_node_clamp_inside (&o, c);
+        lowest = ntreeid;
+      }
+      else {
+        P4EST_ASSERT (lowest == ntreeid);
+        p4est_node_clamp_inside (&o, &tmpq);
+        if (p4est_quadrant_compare (&tmpq, c) < 0) {
+          /* same tree (periodic) and the new position is lower than the old */
+          *c = tmpq;
+        }
+      }
+    }
+  }
+  sc_array_reset (eta);
+  eta = NULL;
+  et = NULL;
+  if (contacts == 2) {
+    goto endfunction;
+  }
+#endif
+
+  P4EST_ASSERT (contacts == P4EST_DIM);
+  cta = &ci.corner_transforms;
+  sc_array_init (cta, sizeof (p4est_corner_transform_t));
+  for (corner = 0; corner < P4EST_CHILDREN; ++corner) {
+    if (!(quad_contact[p4est_corner_faces[corner][0]] &&
+          quad_contact[p4est_corner_faces[corner][1]] &&
+#ifdef P4_TO_P8
+          quad_contact[p4est_corner_faces[corner][2]] &&
+#endif
+          1)) {
+      continue;
+    }
+    p4est_find_corner_transform (conn, treeid, corner, &ci);
+    for (ctreez = 0; ctreez < cta->elem_count; ++ctreez) {
+      ct = p4est_corner_array_index (cta, ctreez);
+      ntreeid = ct->ntree;
+      if (ntreeid > lowest) {
+        /* This neighbor tree is higher, so we keep the ownership */
+        continue;
+      }
+      o.level = P4EST_MAXLEVEL;
+      p4est_quadrant_transform_corner (&o, (int) ct->ncorner, 0);
+      if (ntreeid < lowest) {
+        p4est_node_clamp_inside (&o, c);
+        lowest = ntreeid;
+      }
+      else {
+        P4EST_ASSERT (lowest == ntreeid);
+        p4est_node_clamp_inside (&o, &tmpq);
+        if (p4est_quadrant_compare (&tmpq, c) < 0) {
+          /* same tree (periodic) and the new position is lower than the old */
+          *c = tmpq;
+        }
+      }
+    }
+  }
+  sc_array_reset (cta);
+
+endfunction:
+  c->p.which_tree = lowest;
+
+  P4EST_ASSERT (p4est_quadrant_is_node (c, 1));
+  P4EST_ASSERT (c->p.which_tree >= 0 && c->p.which_tree < conn->num_trees);
+}
+
+static int
+p4est_nodes_foreach (void **item, const void *u)
+{
+  const sc_hash_array_data_t *internal_data =
+    (const sc_hash_array_data_t *) u;
+  const p4est_locidx_t *new_node_number =
+    (const p4est_locidx_t *) internal_data->user_data;
+
+  *item = (void *) (long) new_node_number[(long) *item];
+
+  return 1;
+}
+
+#ifdef P4EST_ENABLE_MPI
+
+static p4est_locidx_t *
+p4est_shared_offsets (sc_array_t * inda)
+{
+  p4est_locidx_t      il, num_indep_nodes;
+  p4est_locidx_t     *shared_offsets;
+  p4est_indep_t      *in;
+
+  num_indep_nodes = (p4est_locidx_t) inda->elem_count;
+  shared_offsets = P4EST_ALLOC (p4est_locidx_t, num_indep_nodes);
+
+  for (il = 0; il < num_indep_nodes; ++il) {
+    in = (p4est_indep_t *) sc_array_index (inda, il);
+    shared_offsets[il] = (p4est_locidx_t) in->pad16;
+    in->pad16 = -1;
+  }
+
+  return shared_offsets;
+}
+
+#endif
+
+p4est_nodes_t      *
+p4est_nodes_new (p4est_t * p4est, p4est_ghost_t * ghost)
+{
+  const int           num_procs = p4est->mpisize;
+  const int           rank = p4est->mpirank;
+#ifdef P4EST_ENABLE_MPI
+  int                 mpiret;
+  int                 owner, prev, start;
+  int                 first_peer, last_peer;
+  int                 num_send_queries, num_send_nonzero, num_recv_queries;
+  int                 byte_count, elem_count;
+  int                 local_send_count, local_recv_count;
+  int                 nwin, maxpeers, maxwin, twomaxwin;
+  int                 my_ranges[2 * p4est_num_ranges];
+  int                *procs, *all_ranges;
+  int                *nonlocal_ranks;
+  int                *old_sharers, *new_sharers;
+  char               *this_base;
+  size_t              first_size, second_size, this_size;
+  size_t              num_sharers, old_position, new_position;
+  p4est_qcoord_t     *xyz;
+  p4est_topidx_t     *ttt;
+  p4est_locidx_t      end_owned_indeps, *shared_offsets;
+  p4est_locidx_t     *node_number;
+  p4est_node_peer_t  *peers, *peer;
+  p4est_indep_t       inkey;
+  sc_array_t          send_requests;
+  sc_recycle_array_t *orarr, *nrarr;
+  MPI_Request        *send_request;
+  MPI_Status          probe_status, recv_status;
+#endif
+#if defined (P4EST_ENABLE_MPI) || defined (P4_TO_P8)
+  int                 l;
+#endif
+  int                 k;
+  int                 qcid, face;
+  int                 clamped = 1;
+  void               *save_user_data;
+  size_t              zz, position;
+  int8_t             *local_status, *quad_status;
+  p4est_topidx_t      jt;
+  p4est_locidx_t      il, first, second;
+  p4est_locidx_t      num_local_nodes, quad_indeps[P4EST_CHILDREN];
+  p4est_locidx_t      num_owned_shared, num_owned_indeps;
+  p4est_locidx_t      offset_owned_indeps;
+  p4est_locidx_t      num_indep_nodes, dup_indep_nodes, all_face_hangings;
+  p4est_locidx_t      num_face_hangings, dup_face_hangings;
+  p4est_locidx_t     *local_nodes, *quad_nodes;
+  p4est_locidx_t     *new_node_number;
+  p4est_tree_t       *tree;
+  p4est_nodes_t      *nodes;
+  p4est_quadrant_t    c, n, p;
+  p4est_quadrant_t   *q, *qpp[3], *r;
+  p4est_indep_t      *in;
+  sc_array_t         *quadrants;
+  sc_array_t         *inda, *faha;
+  sc_array_t         *shared_indeps;
+  sc_hash_array_t    *indep_nodes;
+  sc_hash_array_t    *face_hangings;
+#ifndef P4_TO_P8
+  p4est_hang2_t      *fh;
+#else
+  int                 edge, corner;
+#ifdef P4EST_ENABLE_DEBUG
+  p4est_locidx_t      num_face_hangings_end;
+#endif
+  p4est_locidx_t      num_edge_hangings_begin;
+  p4est_locidx_t      num_edge_hangings, dup_edge_hangings;
+  p8est_hang4_t      *fh;
+  p8est_hang2_t      *eh;
+  sc_array_t          exist_array;
+  sc_array_t         *edha;
+  sc_hash_array_t    *edge_hangings;
+#endif
+
+  if (ghost == NULL)
+    return p4est_nodes_new_local (p4est);
+
+  P4EST_GLOBAL_PRODUCTION ("Into " P4EST_STRING "_nodes_new\n");
+  p4est_log_indent_push ();
+  P4EST_ASSERT (p4est_is_valid (p4est));
+
+  P4EST_QUADRANT_INIT (&c);
+  P4EST_QUADRANT_INIT (&n);
+  P4EST_QUADRANT_INIT (&p);
+  qpp[0] = NULL;
+  qpp[1] = qpp[2] = &p;
+
+  /* allocate and initialize the node structure to return */
+  nodes = P4EST_ALLOC (p4est_nodes_t, 1);
+  memset (nodes, -1, sizeof (*nodes));
+  faha = &nodes->face_hangings;
+#ifdef P4_TO_P8
+  edha = &nodes->edge_hangings;
+#endif
+  shared_indeps = &nodes->shared_indeps;
+  sc_array_init (shared_indeps, sizeof (sc_recycle_array_t));
+  nodes->shared_offsets = NULL;
+
+  /* compute number of local quadrant corners */
+  nodes->num_local_quadrants = p4est->local_num_quadrants;
+  num_local_nodes =             /* same type */
+    P4EST_CHILDREN * nodes->num_local_quadrants;
+
+  /* Store hanging node status:
+   * 0 for independent, 1 for face hanging, 2 for edge hanging.
+   */
+  local_status = P4EST_ALLOC (int8_t, num_local_nodes);
+  memset (local_status, -1, num_local_nodes * sizeof (*local_status));
+
+  /* Store the local node index for each corner of the elements.
+   */
+  nodes->local_nodes = local_nodes =
+    P4EST_ALLOC (p4est_locidx_t, num_local_nodes);
+  memset (local_nodes, -1, num_local_nodes * sizeof (*local_nodes));
+
+  indep_nodes = sc_hash_array_new (sizeof (p4est_indep_t),
+                                   p4est_node_hash_piggy_fn,
+                                   p4est_node_equal_piggy_fn, &clamped);
+#ifndef P4_TO_P8
+  face_hangings = sc_hash_array_new (sizeof (p4est_hang2_t),
+                                     p4est_node_hash_piggy_fn,
+                                     p4est_node_equal_piggy_fn, &clamped);
+#else
+  face_hangings = sc_hash_array_new (sizeof (p8est_hang4_t),
+                                     p4est_node_hash_piggy_fn,
+                                     p4est_node_equal_piggy_fn, &clamped);
+  edge_hangings = sc_hash_array_new (sizeof (p8est_hang2_t),
+                                     p4est_node_hash_piggy_fn,
+                                     p4est_node_equal_piggy_fn, &clamped);
+  sc_array_init (&exist_array, sizeof (int));
+#endif
+
+  /* This first loop will fill the local_status array with hanging status.
+   * It will also collect all independent nodes relevant for the elements.
+   */
+  num_indep_nodes = dup_indep_nodes = all_face_hangings = 0;
+  quad_nodes = local_nodes;
+  quad_status = local_status;
+  for (jt = p4est->first_local_tree; jt <= p4est->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    quadrants = &tree->quadrants;
+
+    /* determine hanging node status and collect all anchored nodes */
+    for (zz = 0; zz < quadrants->elem_count;
+         quad_nodes += P4EST_CHILDREN, quad_status += P4EST_CHILDREN, ++zz) {
+      qpp[0] = q = p4est_quadrant_array_index (quadrants, zz);
+      qcid = p4est_quadrant_child_id (q);
+      if (q->level > 0) {
+        p4est_quadrant_parent (q, &p);
+      }
+#ifdef P4EST_ENABLE_DEBUG
+      else {
+        P4EST_QUADRANT_INIT (&p);
+      }
+#endif
+
+      /* assign independent node and face hanging node status */
+      for (k = 0; k < P4EST_CHILDREN; ++k) {
+        if (k == qcid || k == P4EST_CHILDREN - 1 - qcid || q->level == 0) {
+          quad_status[k] = 0;   /* independent node */
+          continue;
+        }
+        face = p4est_child_corner_faces[qcid][k];
+        if (face == -1) {
+#ifndef P4_TO_P8
+          SC_ABORT_NOT_REACHED ();
+#else
+          P4EST_ASSERT (p8est_child_corner_edges[qcid][k] >= 0);
+          continue;
+#endif
+        }
+        p4est_quadrant_face_neighbor (&p, face, &n);
+        if (p4est_quadrant_exists (p4est, ghost, jt, &n, NULL, NULL, NULL)) {
+          quad_status[k] = 1;   /* face hanging node */
+#ifdef P4_TO_P8
+          for (l = 0; l < P4EST_HALF; ++l) {
+            corner = p4est_face_corners[face][l];
+            if (corner != qcid && corner != k) {
+              quad_status[corner] = 2;  /* identify edge hanging nodes */
+            }
+          }
+#endif
+          ++all_face_hangings;
+        }
+        else {
+          quad_status[k] = 0;   /* independent node */
+        }
+      }
+
+#ifdef P4_TO_P8
+      /* assign edge hanging node status */
+      for (k = 0; k < P4EST_CHILDREN; ++k) {
+        if (quad_status[k] == -1) {
+          edge = p8est_child_corner_edges[qcid][k];
+          P4EST_ASSERT (edge >= 0 && edge < P8EST_EDGES);
+          p8est_quadrant_edge_neighbor (&p, edge, &n);
+          quad_status[k] = (int8_t)
+            (p4est_quadrant_exists (p4est, ghost, jt, &n,
+                                    &exist_array, NULL, NULL) ? 2 : 0);
+        }
+      }
+#endif
+
+      /* collect all independent nodes related to the element */
+      for (k = 0; k < P4EST_CHILDREN; ++k) {
+        P4EST_ASSERT (quad_status[k] >= 0 || quad_status[k] <= 2);
+        p4est_quadrant_corner_node (qpp[quad_status[k]], k, &n);
+        p4est_node_canonicalize (p4est, jt, &n, &c);
+        r =
+          (p4est_quadrant_t *) sc_hash_array_insert_unique (indep_nodes, &c,
+                                                            &position);
+        if (r != NULL) {
+          *r = c;
+          P4EST_ASSERT (num_indep_nodes == (p4est_locidx_t) position);
+          ++num_indep_nodes;
+        }
+        else {
+          ++dup_indep_nodes;
+        }
+        P4EST_ASSERT ((p4est_locidx_t) position < num_indep_nodes);
+        quad_nodes[k] = (p4est_locidx_t) position;
+      }
+    }
+  }
+  P4EST_ASSERT (num_indep_nodes + dup_indep_nodes == num_local_nodes);
+#ifdef P4_TO_P8
+  sc_array_reset (&exist_array);
+#endif
+  inda = &indep_nodes->a;
+  P4EST_ASSERT (num_indep_nodes == (p4est_locidx_t) inda->elem_count);
+
+  /* Reorder independent nodes by their global treeid and z-order index. */
+  new_node_number = P4EST_ALLOC (p4est_locidx_t, num_indep_nodes);
+  for (il = 0; il < num_indep_nodes; ++il) {
+    in = (p4est_indep_t *) sc_array_index (inda, (size_t) il);
+    in->pad8 = 0;               /* shared by 0 other processors so far */
+    in->pad16 = (int16_t) (-1);
+    in->p.piggy3.local_num = il;
+  }
+  sc_array_sort (inda, p4est_quadrant_compare_piggy);
+  for (il = 0; il < num_indep_nodes; ++il) {
+    in = (p4est_indep_t *) sc_array_index (inda, (size_t) il);
+    new_node_number[in->p.piggy3.local_num] = il;
+#ifndef P4EST_ENABLE_MPI
+    in->p.piggy3.local_num = il;
+#endif
+  }
+
+  /* Re-synchronize hash array and local nodes */
+  save_user_data = indep_nodes->internal_data.user_data;
+  indep_nodes->internal_data.user_data = new_node_number;
+  sc_hash_foreach (indep_nodes->h, p4est_nodes_foreach);
+  indep_nodes->internal_data.user_data = save_user_data;
+  for (il = 0; il < num_local_nodes; ++il) {
+    P4EST_ASSERT (local_nodes[il] >= 0 && local_nodes[il] < num_indep_nodes);
+    local_nodes[il] = new_node_number[local_nodes[il]];
+  }
+#ifndef P4EST_ENABLE_MPI
+  num_owned_indeps = num_indep_nodes;
+  offset_owned_indeps = 0;
+#else
+  num_owned_indeps = 0;         /* will be computed below */
+  offset_owned_indeps = -1;     /* will be computed below */
+#endif
+  num_owned_shared = 0;
+  P4EST_FREE (new_node_number);
+
+#ifdef P4EST_ENABLE_MPI
+  /* Fill send buffers and number owned nodes. */
+  first_size = P4EST_DIM * sizeof (p4est_qcoord_t) + sizeof (p4est_topidx_t);
+  first_size = SC_MAX (first_size, sizeof (p4est_locidx_t));
+  procs = P4EST_ALLOC_ZERO (int, num_procs);
+  peers = P4EST_ALLOC (p4est_node_peer_t, num_procs);
+  sc_array_init (&send_requests, sizeof (MPI_Request));
+  for (k = 0; k < num_procs; ++k) {
+    peer = peers + k;
+    peer->expect_query = peer->expect_reply = 0;
+    peer->recv_offset = 0;
+    sc_array_init (&peer->send_first, first_size);
+    sc_array_init (&peer->recv_first, first_size);
+    sc_array_init (&peer->send_second, 1);
+    sc_array_init (&peer->recv_second, 1);
+  }
+  first_peer = num_procs;
+  last_peer = -1;
+  prev = 0;
+  for (il = 0; il < num_indep_nodes; ++il) {
+    in = (p4est_indep_t *) sc_array_index (inda, (size_t) il);
+    owner = p4est_comm_find_owner (p4est, in->p.which_tree,
+                                   (p4est_quadrant_t *) in, prev);
+    if (owner != rank) {
+      peer = peers + owner;
+      xyz = (p4est_qcoord_t *) sc_array_push (&peer->send_first);
+      xyz[0] = in->x;
+      xyz[1] = in->y;
+#ifdef P4_TO_P8
+      xyz[2] = in->z;
+#endif
+      ttt = (p4est_topidx_t *) (&xyz[P4EST_DIM]);
+      *ttt = in->p.which_tree;
+      in->p.piggy1.owner_rank = owner;
+      if (first_peer == num_procs) {
+        first_peer = owner;
+      }
+      if (owner > rank && offset_owned_indeps == -1) {
+        /* we own no independent nodes and have just found the right offset */
+        offset_owned_indeps = il;
+      }
+      last_peer = owner;
+      ++procs[owner];
+    }
+    else {
+      if (offset_owned_indeps == -1) {
+        /* we own at least one independent node */
+        offset_owned_indeps = il;
+      }
+      in->p.piggy3.local_num = num_owned_indeps++;
+    }
+    P4EST_ASSERT (prev <= owner);
+    prev = owner;
+  }
+  if (offset_owned_indeps == -1) {
+    /* we own no independent nodes and all nodes belong to smaller processors */
+    P4EST_ASSERT (num_owned_indeps == 0);
+    offset_owned_indeps = num_indep_nodes;
+  }
+  end_owned_indeps = offset_owned_indeps + num_owned_indeps;
+
+  /* Distribute global information about who is sending to who. */
+  maxpeers = first_peer;
+  maxwin = last_peer;
+  nwin = sc_ranges_adaptive (p4est_package_id,
+                             p4est->mpicomm, procs, &maxpeers, &maxwin,
+                             p4est_num_ranges, my_ranges, &all_ranges);
+  twomaxwin = 2 * maxwin;
+#ifdef P4EST_ENABLE_DEBUG
+  P4EST_GLOBAL_STATISTICSF ("Max peers %d ranges %d/%d\n",
+                            maxpeers, maxwin, p4est_num_ranges);
+  sc_ranges_statistics (p4est_package_id, SC_LP_STATISTICS,
+                        p4est->mpicomm, num_procs, procs,
+                        rank, p4est_num_ranges, my_ranges);
+#endif
+  P4EST_VERBOSEF ("Peer ranges %d/%d/%d first %d last %d owned %lld/%lld\n",
+                  nwin, maxwin, p4est_num_ranges, first_peer, last_peer,
+                  (long long) num_owned_indeps, (long long) num_indep_nodes);
+
+  /* Send queries to the owners of the independent nodes that I share. */
+  num_send_queries = num_send_nonzero = local_send_count = 0;
+  for (l = 0; l < nwin; ++l) {
+    for (k = my_ranges[2 * l]; k <= my_ranges[2 * l + 1]; ++k) {
+      peer = peers + k;
+      if (k == rank) {
+        P4EST_ASSERT (peer->send_first.elem_count == 0);
+        continue;
+      }
+      send_request = (MPI_Request *) sc_array_push (&send_requests);
+      this_size = peer->send_first.elem_count * first_size;
+      mpiret = MPI_Isend (peer->send_first.array, (int) this_size,
+                          MPI_BYTE, k, P4EST_COMM_NODES_QUERY,
+                          p4est->mpicomm, send_request);
+      SC_CHECK_MPI (mpiret);
+      local_send_count += (int) peer->send_first.elem_count;
+      ++num_send_queries;
+      if (this_size > 0) {
+        ++num_send_nonzero;
+        peer->expect_reply = 1;
+      }
+    }
+  }
+
+  /* Prepare to receive queries */
+  num_recv_queries = local_recv_count = 0;
+  for (k = 0; k < num_procs; ++k) {
+    if (k == rank) {
+      continue;
+    }
+    for (l = 0; l < maxwin; ++l) {
+      start = all_ranges[k * twomaxwin + 2 * l];
+      if (start == -1 || start > rank) {
+        break;
+      }
+      if (rank <= all_ranges[k * twomaxwin + 2 * l + 1]) {
+        peers[k].expect_query = 1;
+        ++num_recv_queries;
+        break;
+      }
+    }
+  }
+  P4EST_VERBOSEF ("Node queries send %d nonz %d recv %d\n",
+                  num_send_queries, num_send_nonzero, num_recv_queries);
+
+  /* Receive queries and look up the reply information */
+  P4EST_QUADRANT_INIT (&inkey);
+  inkey.level = P4EST_MAXLEVEL;
+  shared_offsets = NULL;
+  for (l = 0; l < num_recv_queries; ++l) {
+    mpiret = MPI_Probe (MPI_ANY_SOURCE, P4EST_COMM_NODES_QUERY,
+                        p4est->mpicomm, &probe_status);
+    SC_CHECK_MPI (mpiret);
+    k = probe_status.MPI_SOURCE;
+    peer = peers + k;
+    P4EST_ASSERT (k != rank && peer->expect_query);
+    mpiret = MPI_Get_count (&probe_status, MPI_BYTE, &byte_count);
+    SC_CHECK_MPI (mpiret);
+    P4EST_ASSERT (byte_count % first_size == 0);
+    elem_count = byte_count / (int) first_size;
+    local_recv_count += elem_count;
+    sc_array_resize (&peer->recv_first, (size_t) elem_count);
+    mpiret = MPI_Recv (peer->recv_first.array, byte_count, MPI_BYTE,
+                       k, P4EST_COMM_NODES_QUERY,
+                       p4est->mpicomm, &recv_status);
+    SC_CHECK_MPI (mpiret);
+    peer->expect_query = 0;
+    for (zz = 0; zz < peer->recv_first.elem_count; ++zz) {
+      xyz = (p4est_qcoord_t *) sc_array_index (&peer->recv_first, zz);
+      inkey.x = xyz[0];
+      inkey.y = xyz[1];
+#ifdef P4_TO_P8
+      inkey.z = xyz[2];
+#endif
+      ttt = (p4est_topidx_t *) (&xyz[P4EST_DIM]);
+      inkey.p.which_tree = *ttt;
+      P4EST_EXECUTE_ASSERT_TRUE (sc_hash_array_lookup
+                                 (indep_nodes, &inkey, &position));
+      P4EST_ASSERT ((p4est_locidx_t) position >= offset_owned_indeps &&
+                    (p4est_locidx_t) position < end_owned_indeps);
+      node_number = (p4est_locidx_t *) xyz;
+      *node_number = (p4est_locidx_t) position - offset_owned_indeps;
+      in = (p4est_indep_t *) sc_array_index (inda, position);
+      P4EST_ASSERT (p4est_node_equal_piggy_fn (&inkey, in, &clamped));
+      P4EST_ASSERT (in->pad8 >= 0);
+      num_sharers = (size_t) in->pad8;
+      P4EST_ASSERT (num_sharers <= shared_indeps->elem_count);
+      SC_CHECK_ABORT (num_sharers < (size_t) INT8_MAX,
+                      "Max independent node sharer limit exceeded");
+      if (num_sharers == shared_indeps->elem_count) {
+        nrarr = (sc_recycle_array_t *) sc_array_push (shared_indeps);
+        sc_recycle_array_init (nrarr, (num_sharers + 1) * sizeof (int));
+      }
+      else {
+        nrarr =
+          (sc_recycle_array_t *) sc_array_index (shared_indeps, num_sharers);
+      }
+      new_sharers = (int *) sc_recycle_array_insert (nrarr, &new_position);
+      if (num_sharers > 0) {
+        if (shared_offsets == NULL) {
+          P4EST_ASSERT (in->pad16 >= 0);
+          old_position = (size_t) in->pad16;
+        }
+        else {
+          P4EST_ASSERT (in->pad16 == -1);
+          old_position = (size_t) shared_offsets[position];
+        }
+        orarr =
+          (sc_recycle_array_t *) sc_array_index (shared_indeps,
+                                                 num_sharers - 1);
+        old_sharers = (int *) sc_recycle_array_remove (orarr, old_position);
+        memcpy (new_sharers, old_sharers, num_sharers * sizeof (int));
+      }
+      else {
+        ++num_owned_shared;
+      }
+      new_sharers[num_sharers] = k;
+      ++in->pad8;
+      if (shared_offsets == NULL) {
+        if (new_position > (size_t) INT16_MAX) {
+          shared_offsets = p4est_shared_offsets (inda);
+          shared_offsets[position] = (p4est_locidx_t) new_position;
+        }
+        else {
+          in->pad16 = (int16_t) new_position;
+        }
+      }
+      else {
+        shared_offsets[position] = (p4est_locidx_t) new_position;
+      }
+    }
+  }
+
+  /* Assemble and send reply information.  This is variable size.
+   * (p4est_locidx_t)      Node number in this processor's ordering
+   * (int8_t)              Number of sharers (not including this processor)
+   * num_sharers * (int)   The ranks of all sharers.
+   */
+  second_size = sizeof (p4est_locidx_t) + sizeof (int8_t);
+  for (k = 0; k < num_procs; ++k) {
+    peer = peers + k;
+    if (peer->recv_first.elem_count == 0) {
+      continue;
+    }
+    P4EST_ASSERT (k != rank);
+    for (zz = 0; zz < peer->recv_first.elem_count; ++zz) {
+      node_number = (p4est_locidx_t *) sc_array_index (&peer->recv_first, zz);
+      position = (size_t) (*node_number + offset_owned_indeps);
+      in = (p4est_indep_t *) sc_array_index (inda, position);
+      P4EST_ASSERT (p4est_quadrant_is_node ((p4est_quadrant_t *) in, 1));
+      P4EST_ASSERT (in->pad8 >= 0);
+      num_sharers = (size_t) in->pad8;
+      P4EST_ASSERT (num_sharers <= shared_indeps->elem_count);
+      this_size = second_size + num_sharers * sizeof (int);
+      this_base =
+        (char *) sc_array_push_count (&peer->send_second, this_size);
+      *(p4est_locidx_t *) this_base = *node_number;
+      *(int8_t *) (this_base + sizeof (p4est_locidx_t)) = in->pad8;
+      if (num_sharers > 0) {
+        if (shared_offsets == NULL) {
+          P4EST_ASSERT (in->pad16 >= 0);
+          new_position = (size_t) in->pad16;
+        }
+        else {
+          P4EST_ASSERT (in->pad16 == -1);
+          new_position = (size_t) shared_offsets[position];
+        }
+        nrarr =
+          (sc_recycle_array_t *) sc_array_index (shared_indeps,
+                                                 num_sharers - 1);
+        new_sharers = (int *) sc_array_index (&nrarr->a, new_position);
+        memcpy (this_base + second_size, new_sharers,
+                num_sharers * sizeof (int));
+      }
+    }
+    send_request = (MPI_Request *) sc_array_push (&send_requests);
+    mpiret = MPI_Isend (peer->send_second.array,
+                        (int) peer->send_second.elem_count,
+                        MPI_BYTE, k, P4EST_COMM_NODES_REPLY,
+                        p4est->mpicomm, send_request);
+    SC_CHECK_MPI (mpiret);
+    sc_array_reset (&peer->recv_first);
+  }
+#endif /* P4EST_ENABLE_MPI */
+
+  /* This second loop will collect and assign all hanging nodes. */
+  num_face_hangings = dup_face_hangings = 0;    /* still unknown */
+#ifdef P4_TO_P8
+  num_edge_hangings = dup_edge_hangings = 0;    /* still unknown */
+  num_edge_hangings_begin = num_indep_nodes + all_face_hangings;
+#endif
+  quad_nodes = local_nodes;
+  quad_status = local_status;
+  for (jt = p4est->first_local_tree; jt <= p4est->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    quadrants = &tree->quadrants;
+
+    /* collect all face and edge hanging nodes */
+    for (zz = 0; zz < quadrants->elem_count;
+         quad_nodes += P4EST_CHILDREN, quad_status += P4EST_CHILDREN, ++zz) {
+      q = p4est_quadrant_array_index (quadrants, zz);
+      qcid = p4est_quadrant_child_id (q);
+
+      /* create hanging nodes and assign related independent nodes */
+      memcpy (quad_indeps, quad_nodes, P4EST_CHILDREN * sizeof (*quad_nodes));
+      for (k = 0; k < P4EST_CHILDREN; ++k) {
+        if (quad_status[k] == 1) {
+          P4EST_ASSERT (qcid != k && quad_indeps[qcid] != quad_indeps[k]);
+          P4EST_ASSERT (p4est_child_corner_faces[qcid][k] >= 0);
+          p4est_quadrant_corner_node (q, k, &n);
+          p4est_node_canonicalize (p4est, jt, &n, &c);
+          r =
+            (p4est_quadrant_t *) sc_hash_array_insert_unique (face_hangings,
+                                                              &c, &position);
+          if (r != NULL) {
+            *r = c;
+            P4EST_ASSERT (num_face_hangings == (p4est_locidx_t) position);
+#ifndef P4_TO_P8
+            fh = (p4est_hang2_t *) r;
+            first = quad_indeps[qcid];
+            second = quad_indeps[k];
+            if (first < second) {
+              fh->p.piggy.depends[0] = first;
+              fh->p.piggy.depends[1] = second;
+            }
+            else {
+              fh->p.piggy.depends[0] = second;
+              fh->p.piggy.depends[1] = first;
+            }
+#else
+            fh = (p8est_hang4_t *) r;
+            fh->p.piggy.depends[0] = quad_indeps[qcid];
+            fh->p.piggy.depends[1] = quad_indeps[k];
+            fh->p.piggy.depends[2] = -1;
+            fh->p.piggy.depends[3] = -1;
+            face = p8est_child_corner_faces[qcid][k];
+            for (l = 0; l < 4; ++l) {
+              corner = p8est_face_corners[face][l];
+              if (corner != qcid && corner != k) {
+                if (fh->p.piggy.depends[2] == -1) {
+                  fh->p.piggy.depends[2] = quad_indeps[corner];
+                }
+                else {
+                  P4EST_ASSERT (fh->p.piggy.depends[3] == -1);
+                  fh->p.piggy.depends[3] = quad_indeps[corner];
+                }
+              }
+            }
+            qsort (fh->p.piggy.depends,
+                   4, sizeof (p4est_locidx_t), p4est_locidx_compare);
+#endif
+            ++num_face_hangings;
+          }
+          else {
+            ++dup_face_hangings;
+          }
+          quad_nodes[k] =       /* same type */
+            num_indep_nodes + (p4est_locidx_t) position;
+        }
+#ifdef P4_TO_P8
+        else if (quad_status[k] == 2) {
+          P4EST_ASSERT (qcid != k && quad_indeps[qcid] != quad_indeps[k]);
+          P4EST_ASSERT (p8est_child_corner_edges[qcid][k] >= 0);
+          p4est_quadrant_corner_node (q, k, &n);
+          p4est_node_canonicalize (p4est, jt, &n, &c);
+          r =
+            (p4est_quadrant_t *) sc_hash_array_insert_unique (edge_hangings,
+                                                              &c, &position);
+          if (r != NULL) {
+            *r = c;
+            P4EST_ASSERT (num_edge_hangings == (p4est_locidx_t) position);
+            eh = (p8est_hang2_t *) r;
+            first = quad_indeps[qcid];
+            second = quad_indeps[k];
+            if (first < second) {
+              eh->p.piggy.depends[0] = first;
+              eh->p.piggy.depends[1] = second;
+            }
+            else {
+              eh->p.piggy.depends[0] = second;
+              eh->p.piggy.depends[1] = first;
+            }
+            ++num_edge_hangings;
+          }
+          else {
+            ++dup_edge_hangings;
+          }
+          quad_nodes[k] =       /* same type */
+            num_edge_hangings_begin + (p4est_locidx_t) position;
+        }
+#endif
+      }
+    }
+  }
+  P4EST_ASSERT (num_face_hangings + dup_face_hangings == all_face_hangings);
+  P4EST_FREE (local_status);
+  sc_hash_array_rip (face_hangings, faha);
+  P4EST_ASSERT (num_face_hangings == (p4est_locidx_t) faha->elem_count);
+#ifdef P4_TO_P8
+  sc_hash_array_rip (edge_hangings, edha);
+  P4EST_ASSERT (num_edge_hangings == (p4est_locidx_t) edha->elem_count);
+
+  /* Correct the offsets of edge hanging nodes */
+#ifdef P4EST_ENABLE_DEBUG
+  num_face_hangings_end = num_indep_nodes + num_face_hangings;
+#endif
+  for (il = 0; il < num_local_nodes; ++il) {
+    if (local_nodes[il] >= num_edge_hangings_begin) {
+      local_nodes[il] -= dup_face_hangings;
+      P4EST_ASSERT (local_nodes[il] >= num_face_hangings_end);
+    }
+    else {
+      P4EST_ASSERT (local_nodes[il] >= 0 &&
+                    local_nodes[il] < num_face_hangings_end);
+    }
+  }
+#endif
+
+  /* Allocate remaining output data structures */
+  nodes->num_owned_indeps = num_owned_indeps;
+  nodes->num_owned_shared = num_owned_shared;
+  nodes->offset_owned_indeps = offset_owned_indeps;
+  sc_hash_array_rip (indep_nodes, inda = &nodes->indep_nodes);
+  nodes->nonlocal_ranks =
+    P4EST_ALLOC (int, num_indep_nodes - num_owned_indeps);
+  nodes->global_owned_indeps = P4EST_ALLOC (p4est_locidx_t, num_procs);
+  nodes->global_owned_indeps[rank] = num_owned_indeps;
+  indep_nodes = NULL;
+
+#ifdef P4EST_ENABLE_MPI
+  nonlocal_ranks = nodes->nonlocal_ranks;
+
+  /* Receive the replies. */
+  for (l = 0; l < num_send_nonzero; ++l) {
+    mpiret = MPI_Probe (MPI_ANY_SOURCE, P4EST_COMM_NODES_REPLY,
+                        p4est->mpicomm, &probe_status);
+    SC_CHECK_MPI (mpiret);
+    k = probe_status.MPI_SOURCE;
+    peer = peers + k;
+    P4EST_ASSERT (k != rank && peer->expect_reply);
+    mpiret = MPI_Get_count (&probe_status, MPI_BYTE, &byte_count);
+    SC_CHECK_MPI (mpiret);
+    sc_array_resize (&peer->recv_second, (size_t) byte_count);
+    mpiret = MPI_Recv (peer->recv_second.array, byte_count, MPI_BYTE,
+                       k, P4EST_COMM_NODES_REPLY,
+                       p4est->mpicomm, &recv_status);
+    SC_CHECK_MPI (mpiret);
+    peer->expect_reply = 0;
+  }
+#endif /* P4EST_ENABLE_MPI */
+
+  /* Convert receive buffers into the output data structures and unclamp. */
+  for (il = 0; il < num_indep_nodes; ++il) {
+    in = (p4est_indep_t *) sc_array_index (inda, (size_t) il);
+    p4est_node_unclamp ((p4est_quadrant_t *) in);
+#ifdef P4EST_ENABLE_MPI
+    if (il >= offset_owned_indeps && il < end_owned_indeps) {
+      continue;
+    }
+    k = in->p.piggy1.owner_rank;
+    P4EST_ASSERT ((k < rank && il < offset_owned_indeps) ||
+                  (k > rank && il >= end_owned_indeps));
+    *nonlocal_ranks++ = k;
+    peer = peers + k;
+    P4EST_ASSERT (peer->recv_offset + second_size <=
+                  peer->recv_second.elem_count);
+    this_base =
+      (char *) sc_array_index (&peer->recv_second, peer->recv_offset);
+    in->p.piggy3.local_num = *(p4est_locidx_t *) this_base;
+    num_sharers =
+      (size_t) (*(int8_t *) (this_base + sizeof (p4est_locidx_t)));
+    P4EST_ASSERT (num_sharers > 0);
+    this_size = second_size + num_sharers * sizeof (int);
+    P4EST_ASSERT (peer->recv_offset + this_size <=
+                  peer->recv_second.elem_count);
+    if (shared_indeps->elem_count < num_sharers) {
+      nrarr = NULL;
+      old_position = shared_indeps->elem_count;
+      sc_array_resize (shared_indeps, num_sharers);
+      for (zz = old_position; zz < num_sharers; ++zz) {
+        nrarr = (sc_recycle_array_t *) sc_array_index (shared_indeps, zz);
+        sc_recycle_array_init (nrarr, (zz + 1) * sizeof (int));
+      }
+    }
+    else {
+      nrarr =
+        (sc_recycle_array_t *) sc_array_index (shared_indeps,
+                                               num_sharers - 1);
+    }
+    new_sharers = (int *) sc_recycle_array_insert (nrarr, &new_position);
+    memcpy (new_sharers, this_base + second_size, num_sharers * sizeof (int));
+    for (zz = 0; zz < num_sharers; ++zz) {
+      if (new_sharers[zz] == rank) {
+        new_sharers[zz] = k;
+        break;
+      }
+    }
+    P4EST_ASSERT (zz < num_sharers);
+    in->pad8 = (int8_t) num_sharers;
+    if (shared_offsets == NULL) {
+      if (new_position > (size_t) INT16_MAX) {
+        shared_offsets = p4est_shared_offsets (inda);
+        shared_offsets[il] = (p4est_locidx_t) new_position;
+      }
+      else {
+        in->pad16 = (int16_t) new_position;
+      }
+    }
+    else {
+      shared_offsets[il] = (p4est_locidx_t) new_position;
+    }
+    peer->recv_offset += this_size;
+#endif /* P4EST_ENABLE_MPI */
+  }
+
+  /* Unclamp the hanging nodes as well. */
+  for (zz = 0; zz < faha->elem_count; ++zz) {
+#ifdef P4_TO_P8
+    fh = (p8est_hang4_t *) sc_array_index (faha, zz);
+#else
+    fh = (p4est_hang2_t *) sc_array_index (faha, zz);
+#endif
+    p4est_node_unclamp ((p4est_quadrant_t *) fh);
+  }
+#ifdef P4_TO_P8
+  for (zz = 0; zz < edha->elem_count; ++zz) {
+    eh = (p8est_hang2_t *) sc_array_index (edha, zz);
+    p4est_node_unclamp ((p4est_quadrant_t *) eh);
+  }
+#endif
+
+#ifdef P4EST_ENABLE_MPI
+  /* Wait and close all send requests. */
+  if (send_requests.elem_count > 0) {
+    mpiret = MPI_Waitall ((int) send_requests.elem_count,
+                          (MPI_Request *) send_requests.array,
+                          MPI_STATUSES_IGNORE);
+    SC_CHECK_MPI (mpiret);
+  }
+  nodes->shared_offsets = shared_offsets;
+
+  /* Clean up allocated communications memory. */
+  SC_FREE (all_ranges);
+  sc_array_reset (&send_requests);
+  for (k = 0; k < num_procs; ++k) {
+    peer = peers + k;
+    P4EST_ASSERT (peer->recv_offset == peer->recv_second.elem_count);
+    sc_array_reset (&peer->send_first);
+    /* peer->recv_first has been reset above */
+    sc_array_reset (&peer->send_second);
+    sc_array_reset (&peer->recv_second);
+  }
+  P4EST_FREE (peers);
+  P4EST_FREE (procs);
+
+  mpiret = MPI_Allgather (&num_owned_indeps, 1, P4EST_MPI_LOCIDX,
+                          nodes->global_owned_indeps, 1, P4EST_MPI_LOCIDX,
+                          p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+#endif /* P4EST_ENABLE_MPI */
+
+  /* Print some statistics and clean up. */
+  P4EST_VERBOSEF ("Collected %lld independent nodes with %lld duplicates\n",
+                  (long long) num_indep_nodes, (long long) dup_indep_nodes);
+  P4EST_VERBOSEF ("Collected %lld face hangings with %lld duplicates\n",
+                  (long long) num_face_hangings,
+                  (long long) dup_face_hangings);
+#ifdef P4_TO_P8
+  P4EST_VERBOSEF ("Collected %lld edge hangings with %lld duplicates\n",
+                  (long long) num_edge_hangings,
+                  (long long) dup_edge_hangings);
+#endif
+#ifdef P4EST_ENABLE_MPI
+  P4EST_VERBOSEF ("Owned nodes %lld/%lld/%lld max sharer count %llu\n",
+                  (long long) num_owned_shared,
+                  (long long) num_owned_indeps,
+                  (long long) num_indep_nodes,
+                  (unsigned long long) shared_indeps->elem_count);
+
+  P4EST_ASSERT (0 <= offset_owned_indeps &&
+                offset_owned_indeps <= end_owned_indeps &&
+                end_owned_indeps <= num_indep_nodes);
+#endif
+
+  P4EST_ASSERT (p4est_nodes_is_valid (p4est, nodes));
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_nodes_new\n");
+
+  return nodes;
+}
+
+void
+p4est_nodes_destroy (p4est_nodes_t * nodes)
+{
+  size_t              zz;
+  sc_recycle_array_t *rarr;
+
+  sc_array_reset (&nodes->indep_nodes);
+  sc_array_reset (&nodes->face_hangings);
+#ifdef P4_TO_P8
+  sc_array_reset (&nodes->edge_hangings);
+#endif
+  P4EST_FREE (nodes->local_nodes);
+
+  for (zz = 0; zz < nodes->shared_indeps.elem_count; ++zz) {
+    rarr = (sc_recycle_array_t *) sc_array_index (&nodes->shared_indeps, zz);
+    sc_recycle_array_reset (rarr);
+  }
+  sc_array_reset (&nodes->shared_indeps);
+  P4EST_FREE (nodes->shared_offsets);
+  P4EST_FREE (nodes->nonlocal_ranks);
+  P4EST_FREE (nodes->global_owned_indeps);
+
+  P4EST_FREE (nodes);
+}
+
+int
+p4est_nodes_is_valid (p4est_t * p4est, p4est_nodes_t * nodes)
+{
+  const int           num_procs = p4est->mpisize;
+  const int           rank = p4est->mpirank;
+  int                 k, prev, owner, pshare, ocount;
+  int                *sharers, *sorted;
+  int                 failed, bad;
+  size_t              zz, position, sharez, num_sharers, max_sharers;
+  p4est_topidx_t      otree, ntree;
+  p4est_locidx_t      il, num_indep_nodes, local_num;
+  p4est_locidx_t      num_owned_indeps, offset_owned_indeps, end_owned_indeps;
+  p4est_indep_t      *in;
+  p4est_quadrant_t   *hq;
+  sc_array_t         *hang;
+  sc_recycle_array_t *rarr;
+
+  failed = 0;
+  sorted = P4EST_ALLOC (int, INT8_MAX);
+
+  if (nodes->indep_nodes.elem_size != sizeof (p4est_indep_t) ||
+#ifndef P4_TO_P8
+      nodes->face_hangings.elem_size != sizeof (p4est_hang2_t) ||
+#else
+      nodes->face_hangings.elem_size != sizeof (p8est_hang4_t) ||
+      nodes->edge_hangings.elem_size != sizeof (p8est_hang2_t) ||
+#endif
+      nodes->shared_indeps.elem_size != sizeof (sc_recycle_array_t)) {
+    P4EST_NOTICE ("p4est nodes invalid array size\n");
+    failed = 1;
+    goto failtest;
+  }
+
+  if (nodes->num_local_quadrants != p4est->local_num_quadrants) {
+    P4EST_NOTICE ("p4est nodes invalid quadrant count\n");
+    failed = 1;
+    goto failtest;
+  }
+
+  max_sharers = nodes->shared_indeps.elem_count;
+  for (zz = 0; zz < max_sharers; ++zz) {
+    rarr = (sc_recycle_array_t *) sc_array_index (&nodes->shared_indeps, zz);
+    num_sharers = zz + 1;
+    for (position = 0; position < rarr->a.elem_count; ++position) {
+      sharers = (int *) sc_array_index (&rarr->a, position);
+      memcpy (sorted, sharers, num_sharers * sizeof (int));
+      qsort (sorted, num_sharers, sizeof (int), sc_int_compare);
+      prev = -1;
+      for (sharez = 0; sharez < num_sharers; ++sharez) {
+        k = sorted[sharez];
+        if (prev >= k || k == rank || k >= num_procs) {
+          P4EST_NOTICE ("p4est nodes invalid sharers 1\n");
+          failed = 1;
+          goto failtest;
+        }
+        prev = k;
+      }
+    }
+  }
+
+  num_indep_nodes = (p4est_locidx_t) nodes->indep_nodes.elem_count;
+  num_owned_indeps = nodes->num_owned_indeps;
+  offset_owned_indeps = nodes->offset_owned_indeps;
+  end_owned_indeps = offset_owned_indeps + num_owned_indeps;
+  k = prev = 0;
+  otree = 0;
+  for (il = 0; il < num_indep_nodes; ++il) {
+    in = (p4est_indep_t *) sc_array_index (&nodes->indep_nodes, (size_t) il);
+    if (!p4est_quadrant_is_node ((p4est_quadrant_t *) in, 0)) {
+      P4EST_NOTICE ("p4est nodes independent clamped\n");
+      failed = 1;
+      goto failtest;
+    }
+    ntree = in->p.piggy3.which_tree;
+    local_num = in->p.piggy3.local_num;
+    if (ntree < otree || ntree >= p4est->connectivity->num_trees) {
+      P4EST_NOTICE ("p4est nodes invalid tree\n");
+      failed = 1;
+      goto failtest;
+    }
+    otree = ntree;
+    if (in->pad8 < 0 || (num_sharers = (size_t) in->pad8) > max_sharers) {
+      P4EST_NOTICE ("p4est nodes invalid sharer count\n");
+      failed = 1;
+      goto failtest;
+    }
+    if (il < offset_owned_indeps || il >= end_owned_indeps) {
+      owner = nodes->nonlocal_ranks[k++];
+      if (owner < prev || owner == rank || owner >= num_procs) {
+        P4EST_NOTICE ("p4est nodes invalid owner\n");
+        failed = 1;
+        goto failtest;
+      }
+      if (local_num < 0 || local_num >= nodes->global_owned_indeps[owner]) {
+        P4EST_NOTICE ("p4est nodes invalid non-owned index\n");
+        failed = 1;
+        goto failtest;
+      }
+      prev = owner;
+      if (num_sharers < 1) {
+        P4EST_NOTICE ("p4est nodes invalid non-owned sharing 1\n");
+        failed = 1;
+        goto failtest;
+      }
+    }
+    else {
+      if (local_num != il - offset_owned_indeps) {
+        P4EST_NOTICE ("p4est nodes invalid owned index\n");
+        failed = 1;
+        goto failtest;
+      }
+      owner = prev = rank;
+    }
+    ocount = 0;
+    if (num_sharers > 0) {
+      rarr =
+        (sc_recycle_array_t *) sc_array_index (&nodes->shared_indeps,
+                                               num_sharers - 1);
+      if (nodes->shared_offsets == NULL) {
+        bad = (in->pad16 < 0);
+        position = (size_t) in->pad16;
+      }
+      else {
+        bad = (in->pad16 != -1);
+        position = (size_t) nodes->shared_offsets[il];
+      }
+      if (bad || position >= rarr->a.elem_count) {
+        P4EST_NOTICE ("p4est nodes invalid sharer position\n");
+        failed = 1;
+        goto failtest;
+      }
+      sharers = (int *) sc_array_index (&rarr->a, position);
+      memcpy (sorted, sharers, num_sharers * sizeof (int));
+      qsort (sorted, num_sharers, sizeof (int), sc_int_compare);
+      pshare = -1;
+      for (zz = 0; zz < num_sharers; ++zz) {
+        if (sorted[zz] <= pshare || sorted[zz] == rank) {
+          P4EST_NOTICE ("p4est nodes invalid sharers 2\n");
+          failed = 1;
+          goto failtest;
+        }
+        if (sorted[zz] == owner) {
+          ++ocount;
+        }
+        pshare = sorted[zz];
+      }
+    }
+    if (owner != rank && ocount != 1) {
+      P4EST_NOTICE ("p4est nodes invalid non-owned sharing 2\n");
+      failed = 1;
+      goto failtest;
+    }
+  }
+
+  /* Test clamp status for hanging nodes */
+  hang = &nodes->face_hangings;
+  for (zz = 0; zz < hang->elem_count; ++zz) {
+    hq = (p4est_quadrant_t *) sc_array_index (hang, zz);
+    if (!p4est_quadrant_is_node (hq, 0)) {
+      P4EST_NOTICE ("p4est nodes face hanging clamped\n");
+      failed = 1;
+      goto failtest;
+    }
+  }
+#ifdef P4_TO_P8
+  hang = &nodes->edge_hangings;
+  for (zz = 0; zz < hang->elem_count; ++zz) {
+    hq = (p8est_quadrant_t *) sc_array_index (hang, zz);
+    if (!p4est_quadrant_is_node (hq, 0)) {
+      P4EST_NOTICE ("p4est nodes edge hanging clamped\n");
+      failed = 1;
+      goto failtest;
+    }
+  }
+#endif
+
+  /* TODO: Test hanging nodes and local corners. */
+
+failtest:
+  P4EST_FREE (sorted);
+
+  return !p4est_comm_sync_flag (p4est, failed, sc_MPI_BOR);
+}
diff --git a/src/p4est_nodes.h b/src/p4est_nodes.h
new file mode 100644
index 0000000..870a534
--- /dev/null
+++ b/src/p4est_nodes.h
@@ -0,0 +1,175 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_NODES_H
+#define P4EST_NODES_H
+
+#include <p4est.h>
+#include <p4est_ghost.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Store an independent node.
+ * Keep this in sync with the p4est_t data structure.
+ */
+typedef struct p4est_indep
+{
+  p4est_qcoord_t      x, y;
+  int8_t              level, pad8;
+  int16_t             pad16;
+  union p4est_indep_data
+  {
+    void               *unused;
+    p4est_topidx_t      which_tree;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      int                 owner_rank;
+    }
+    piggy1;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_topidx_t      from_tree;
+    }
+    piggy_unused2;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      local_num;
+    }
+    piggy3;
+  }
+  p;
+}
+p4est_indep_t;
+
+/** Store a hanging node that depends on two independent nodes.
+ * Keep this in sync with the p4est_t data structure.
+ */
+typedef struct p4est_hang2
+{
+  p4est_qcoord_t      x, y;
+  int8_t              level, pad8;
+  int16_t             pad16;
+  union p4est_hang2_data
+  {
+    void               *unused;
+    p4est_topidx_t      which_tree;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      int                 owner_rank;
+    }
+    piggy_unused1;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_topidx_t      from_tree;
+    }
+    piggy_unused2;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      local_num;
+    }
+    piggy_unused3;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      depends[2];
+    }
+    piggy;
+  }
+  p;
+}
+p4est_hang2_t;
+
+/** This structure holds complete parallel node information.
+ *
+ * Nodes are unique and either independent or face hanging.
+ * Independent nodes store their owner's tree id in piggy3.which_tree.
+ * The index in their owner's ordering is stored in piggy3.local_num.
+ * Hanging nodes store their owner's tree id in piggy.which_tree.
+ * The numbers of their associated independent nodes are in piggy.depends[].
+ *
+ * The local_nodes table is of dimension 4 * num_local_quadrants
+ * and encodes the node indexes for all corners of all quadrants.  Let
+ * ni := indep_nodes.elem_count,
+ * fi := face_hangings.elem_count,
+ * If for l := local_nodes[k]
+ * l >= 0 && l < ni: l indexes into indep_nodes.
+ * l >= ni && l < ni + fi: l - ni indexes into face_hangings.
+ * No other values for l are permitted.
+ *
+ * The array shared_indeps holds lists of node sharers (not including rank).
+ * The entry shared_indeps[i] is of type sc_recycle_array_t
+ * and holds the list of nodes with i + 1 sharers.
+ * For each independent node, its member pad8 holds the number of sharers
+ * and its member pad16 holds the position in the assigned recycle array
+ * if this number fits into an int16_t.  If this limit is exceeded, the
+ * array shared_offsets is filled with these positions as one p4est_locidx_t
+ * per independent node, and all pad16 members are set to -1.  To recognize
+ * the latter situation you can check for shared_offsets != NULL.
+ *
+ * Each processor owns num_owned_indeps of the stored independent nodes.
+ * The first independent owned node is at index offset_owned_indeps.
+ * The table nonlocal_ranks contains the ranks of all stored non-owned nodes.
+ * The table global_owned_indeps holds the number of owned nodes for each rank.
+ */
+typedef struct p4est_nodes
+{
+  p4est_locidx_t      num_local_quadrants;
+  p4est_locidx_t      num_owned_indeps, num_owned_shared;
+  p4est_locidx_t      offset_owned_indeps;
+  sc_array_t          indep_nodes;
+  sc_array_t          face_hangings;
+  p4est_locidx_t     *local_nodes;
+  sc_array_t          shared_indeps;
+  p4est_locidx_t     *shared_offsets;
+  int                *nonlocal_ranks;
+  p4est_locidx_t     *global_owned_indeps;
+}
+p4est_nodes_t;
+
+/** Create node information.
+ * \param [in] ghost    Ghost layer.  If this is NULL, then only
+ *                      tree- and processor-local nodes will be matched
+ *                      and all others duplicated, all nodes will be
+ *                      counted as independent with no sharers, and
+ *                      nodes->global_owned_indeps will be NULL;
+ *                      this also works for a corner-unbalanced forest,
+ *                      but nodes may not be numbered uniquely in this case.
+ */
+p4est_nodes_t      *p4est_nodes_new (p4est_t * p4est, p4est_ghost_t * ghost);
+
+/** Destroy node information. */
+void                p4est_nodes_destroy (p4est_nodes_t * nodes);
+
+/** Check node information for internal consistency. */
+int                 p4est_nodes_is_valid (p4est_t * p4est,
+                                          p4est_nodes_t * nodes);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_NODES_H */
diff --git a/src/p4est_plex.c b/src/p4est_plex.c
new file mode 100644
index 0000000..dcb2d90
--- /dev/null
+++ b/src/p4est_plex.c
@@ -0,0 +1,1511 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_lnodes.h>
+#include <p4est_plex.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_lnodes.h>
+#include <p8est_plex.h>
+#endif
+
+/** Decode the information from p{4,8}est_lnodes_t for a given element.
+ *
+ * \see p4est_lnodes.h for an in-depth discussion of the encoding.
+ * \param [in] face_code         Bit code as defined in p{4,8}est_lnodes.h.
+ * \param [out] hanging_corner   Undefined if no node is hanging.
+ *                               If any node is hanging, this contains
+ *                               one integer per corner, which is -1
+ *                               for corners that are not hanging,
+ *                               and the number of the non-hanging
+ *                               corner on the hanging face/edge otherwise.
+ *                               For faces in 3D, it is diagonally opposite.
+ * \return true if any node is hanging, false otherwise.
+ */
+static int
+lnodes_decode2 (p4est_lnodes_code_t face_code,
+                int hanging_corner[P4EST_CHILDREN])
+{
+  int                 ones = P4EST_CHILDREN - 1;
+  if (face_code) {
+    const int           c = (int) (face_code & ones);
+    int                 i, h;
+    int                 work = (int) (face_code >> P4EST_DIM);
+
+    /* These two corners are never hanging by construction. */
+    hanging_corner[c] = hanging_corner[c ^ ones] = -1;
+    for (i = 0; i < P4EST_DIM; ++i) {
+      /* Process face hanging corners. */
+      h = c ^ (1 << i);
+      hanging_corner[h ^ ones] = (work & 1) ? c : -1;
+#ifdef P4_TO_P8
+      /* Process edge hanging corners. */
+      hanging_corner[h] = (work & P4EST_CHILDREN) ? c : -1;
+#endif
+      work >>= 1;
+    }
+    return 1;
+  }
+  return 0;
+}
+
+static void
+mark_parent (p4est_locidx_t qid, int ctype_int, p4est_lnodes_code_t * F,
+             p4est_locidx_t * quad_to_local, int8_t * is_parent,
+             int8_t * node_dim)
+{
+#ifndef P4_TO_P8
+  int                 dim_limits[3] = { 0, 4, 8 };
+#else
+  int                 dim_limits[4] = { 0, 6, 18, 26 };
+#endif
+  int                 hanging[2][12];
+  int                 has_hanging;
+  int                 c, V = dim_limits[ctype_int];
+
+#ifndef P4_TO_P8
+  has_hanging = p4est_lnodes_decode (F[qid], &hanging[0][0]);
+#else
+  has_hanging = p8est_lnodes_decode (F[qid], &hanging[0][0], &hanging[1][0]);
+#endif
+  if (has_hanging) {
+    int                 climit;
+
+    /* no corners */
+    climit = SC_MIN (P4EST_DIM - 1, ctype_int);
+    for (c = 0; c < climit; c++) {
+      int                 v, vstart = dim_limits[c];
+      int                 vend = dim_limits[c + 1];
+
+      for (v = vstart; v < vend; v++) {
+        if (hanging[c][v - vstart] >= 0) {
+          is_parent[quad_to_local[qid * V + v]] = 1;
+        }
+      }
+    }
+  }
+  for (c = 0; c < ctype_int; c++) {
+    int                 v, vstart = dim_limits[c];
+    int                 vend = dim_limits[c + 1];
+
+    for (v = vstart; v < vend; v++) {
+      node_dim[quad_to_local[qid * V + v]] = P4EST_DIM - 1 - c;
+    }
+  }
+}
+
+static void
+fill_orientations (p4est_quadrant_t * q, p4est_topidx_t t,
+                   p4est_connectivity_t * conn, int8_t * quad_to_orientations)
+{
+  int                 f;
+#ifdef P4_TO_P8
+  int                 e;
+#endif
+
+  for (f = 0; f < P4EST_FACES; f++) {
+    p4est_quadrant_t    tempq;
+
+    p4est_quadrant_face_neighbor (q, f, &tempq);
+    quad_to_orientations[f] = 0;
+    if (p4est_quadrant_is_outside_face (&tempq)) {
+      p4est_topidx_t      nt;
+      int                 nf, o;
+
+      nt = conn->tree_to_tree[P4EST_FACES * t + f];
+      nf = conn->tree_to_face[P4EST_FACES * t + f];
+      o = nf / P4EST_FACES;
+      nf = nf % P4EST_FACES;
+      if (nt != t && nf != f) {
+        if (nt < t || (nt == t && nf < f)) {
+          int                 set;
+#ifdef P4_TO_P8
+          int                 ref;
+#endif
+
+#ifndef P4_TO_P8
+          set = o;
+#else
+          ref = p8est_face_permutation_refs[f][nf];
+          set = p8est_face_permutation_sets[ref][o];
+#endif
+          quad_to_orientations[f] = set;
+        }
+      }
+    }
+  }
+#ifdef P4_TO_P8
+  for (e = 0; e < P8EST_EDGES; e++) {
+    p4est_quadrant_t    tempq;
+
+    p8est_quadrant_edge_neighbor (q, e, &tempq);
+    quad_to_orientations[P4EST_FACES + e] = 0;
+    if (p4est_quadrant_is_outside_face (&tempq)) {
+      int                 set;
+      int                 i, f = -1;
+      int                 cid[2];
+
+      for (i = 0; i < 2; i++) {
+        int                 dir;
+        p4est_qcoord_t      d = 0;
+
+        f = p8est_edge_faces[e][i];
+        dir = f / 2;
+        switch (dir) {
+        case 0:
+          d = tempq.x;
+          break;
+        case 1:
+          d = tempq.y;
+          break;
+        case 2:
+          d = tempq.z;
+          break;
+        default:
+          SC_ABORT_NOT_REACHED ();
+          break;
+        }
+        if (d < 0 || d >= P4EST_ROOT_LEN) {
+          break;
+        }
+      }
+      P4EST_ASSERT (f >= 0);
+
+      set = quad_to_orientations[f];
+      for (i = 0; i < 2; i++) {
+        int                 c, face_ex, face_in;
+
+        c = p8est_edge_corners[e][i];
+        face_ex = p8est_corner_face_corners[c][f];
+        P4EST_ASSERT (face_ex >= 0);
+        face_in = p8est_face_permutations[set][face_ex];
+        cid[i] = face_in;
+      }
+
+      if (cid[0] < cid[1]) {
+        quad_to_orientations[P4EST_FACES + e] = 0;
+      }
+      else {
+        quad_to_orientations[P4EST_FACES + e] = 1;
+      }
+    }
+    else if (p8est_quadrant_is_outside_edge (&tempq)) {
+      int                 edge =
+        conn->tree_to_edge ? conn->tree_to_edge[t * P8EST_EDGES + e] : -1;
+
+      if (edge >= 0) {
+        int                 estart, eend, i;
+
+        estart = conn->ett_offset[edge];
+        eend = conn->ett_offset[edge + 1];
+        for (i = estart; i < eend; i++) {
+          p4est_topidx_t      nt;
+          int8_t              te;
+
+          nt = conn->edge_to_tree[i];
+          te = conn->edge_to_edge[i];
+          if (nt == t && (te % P8EST_EDGES == e)) {
+            quad_to_orientations[P4EST_FACES + e] = te / P8EST_EDGES;
+            break;
+          }
+        }
+        P4EST_ASSERT (i < eend);
+      }
+      else {
+        p4est_locidx_t      ownt = t;
+        int                 owne = e;
+        int                 i, j, o = 0;
+        for (i = 0; i < 2; i++) {
+          p4est_locidx_t      nt;
+          int8_t              nf;
+          int                 fo;
+          int                 ref, set;
+          int                 cid[2];
+          int                 ne;
+
+          f = p8est_edge_faces[e][i];
+          nt = conn->tree_to_tree[P4EST_FACES * t + f];
+          nf = conn->tree_to_face[P4EST_FACES * t + f];
+          fo = nf / P8EST_FACES;
+          nf = nf % P8EST_FACES;
+
+          ref = p8est_face_permutation_refs[f][nf];
+          set = p8est_face_permutation_sets[ref][fo];
+          for (j = 0; j < 2; j++) {
+            int                 c, face_ex, face_in;
+
+            c = p8est_edge_corners[e][j];
+            face_ex = p8est_corner_face_corners[c][f];
+            P4EST_ASSERT (face_ex >= 0);
+            face_in = p8est_face_permutations[set][face_ex];
+            cid[j] = p8est_face_corners[nf][face_in];
+          }
+          ne = p8est_child_corner_edges[cid[0]][cid[1]];
+          P4EST_ASSERT (ne >= 0);
+          if (nt < ownt || (nt == ownt && ne < owne)) {
+            ownt = nt;
+            owne = e;
+            o = (cid[0] < cid[1]) ? 0 : 1;
+          }
+        }
+        quad_to_orientations[P4EST_FACES + e] = o;
+      }
+    }
+  }
+#endif
+}
+
+static void
+parent_to_child (p4est_quadrant_t * q, p4est_topidx_t t, p4est_locidx_t qid,
+                 int ctype_int, p4est_lnodes_code_t * F,
+                 p4est_locidx_t * quad_to_local,
+                 int8_t * quad_to_orientations,
+                 int8_t * quad_to_orientations_orig, int8_t * referenced,
+                 int8_t * node_dim, p4est_locidx_t * child_offsets,
+                 p4est_locidx_t * child_to_id, p4est_connectivity_t * conn)
+{
+#ifndef P4_TO_P8
+  int                 dim_limits[3] = { 0, 4, 8 };
+  int                 no = P4EST_FACES;
+#else
+  int                 dim_limits[4] = { 0, 6, 18, 26 };
+  int                 no = P4EST_FACES + P8EST_EDGES;
+#endif
+  int                 hanging[3][12];
+  int                 has_hanging;
+  int                 f, V = dim_limits[ctype_int];
+
+#ifndef P4_TO_P8
+  has_hanging = p4est_lnodes_decode (F[qid], &hanging[0][0]);
+#else
+  has_hanging = p8est_lnodes_decode (F[qid], &hanging[0][0], &hanging[1][0]);
+#endif
+  has_hanging |= lnodes_decode2 (F[qid], &hanging[P4EST_DIM - 1][0]);
+  fill_orientations (q, t, conn, &quad_to_orientations[qid * no]);
+  if (has_hanging) {
+    int                 c, cid = p4est_quadrant_child_id (q), v;
+
+    if (quad_to_orientations_orig) {
+      p4est_quadrant_t    tempq;
+
+      p4est_quadrant_parent (q, &tempq);
+      fill_orientations (&tempq, t, conn,
+                         &quad_to_orientations_orig[qid * no]);
+      for (f = 0; f < P4EST_FACES; f++) {
+        if (hanging[0][f] < 0) {
+          quad_to_orientations_orig[qid * no + f] = -1;
+        }
+      }
+#ifdef P4_TO_P8
+      for (f = 0; f < P8EST_EDGES; f++) {
+        if (hanging[1][f] < 0) {
+          quad_to_orientations_orig[qid * no + P4EST_FACES + f] = -1;
+        }
+      }
+#endif
+    }
+    /* no corners */
+    for (c = ctype_int - 1; c >= 0; c--) {
+      int                 vstart = dim_limits[c];
+      int                 vend = dim_limits[c + 1];
+
+      if (!c) {
+        for (v = vstart; v < vend; v++) {
+          if (hanging[0][v] >= 0) {
+            int                 o = quad_to_orientations[qid * no + v];
+            int                 childid = hanging[0][v];
+            p4est_locidx_t      child;
+
+#ifndef P4_TO_P8
+            childid ^= o;
+#else
+            childid = p8est_face_permutations[o][childid];
+#endif
+            child = child_offsets[quad_to_local[qid * V + v]] + childid;
+            quad_to_local[qid * V + v] = child;
+            referenced[child] = 1;
+          }
+        }
+      }
+      else if (c == P4EST_DIM - 1) {
+        for (v = vstart; v < vend; v++) {
+          int                 corner = v - vstart;
+          if (hanging[P4EST_DIM - 1][corner] >= 0) {
+            p4est_locidx_t      child = -1;
+            int                 dim;
+
+            f = p4est_child_corner_faces[cid][corner];
+            P4EST_ASSERT (P4EST_DIM == 3 || f >= 0);
+            if (f >= 0) {
+              dim = P4EST_DIM - 1;
+              child = child_offsets[quad_to_local[qid * V + f]];
+            }
+#ifdef P4_TO_P8
+            else {
+              int                 e = p8est_child_corner_edges[cid][corner];
+
+              P4EST_ASSERT (e >= 0);
+              dim = 1;
+              child = child_offsets[quad_to_local[qid * V + e + P4EST_FACES]];
+            }
+#endif
+            P4EST_ASSERT (dim == 1 || dim == 2);
+            child += (dim == 1) ? 2 : 8;
+            quad_to_local[qid * V + v] = child;
+            referenced[child] = 1;
+          }
+        }
+      }
+#ifdef P4_TO_P8
+      else {
+        for (v = vstart; v < vend; v++) {
+          int                 edge = v - vstart;
+          int                 o =
+            quad_to_orientations[qid * no + P4EST_FACES + edge];
+
+          if (hanging[1][edge] >= 0) {
+            p4est_locidx_t      child;
+
+            if (hanging[1][edge] < 4) {
+              int                 h = hanging[1][edge] % 2;
+
+              /* TODO: reconcile intrinsic/extrinsic order */
+              child = child_offsets[quad_to_local[qid * V + v]] + (h ^ o);
+              quad_to_local[qid * V + v] = child;
+              referenced[child] = 1;
+            }
+            else {
+              int                 i;
+
+              for (i = 0; i < 2; i++) {
+                int                 f = p8est_edge_faces[edge][i];
+                int                 ch = p8est_corner_face_corners[cid][f];
+                int                 e, j;
+
+                if (ch >= 0) {
+                  int                 fo = quad_to_orientations[qid * no + f];
+                  int                 dir;
+                  int                 hc =
+                    p8est_face_permutations[fo][hanging[0][f]];
+                  int                 he[2];
+                  int                 child_edge;
+                  int                 cid[2];
+                  int                 diff;
+
+                  P4EST_ASSERT (hanging[0][f] >= 0);
+
+                  he[0] = (hc & 1);
+                  he[1] = 2 + ((hc & 2) >> 1);
+
+                  for (j = 0; j < 4; j++) {
+                    e = p8est_face_edges[f][j];
+
+                    if (e == edge) {
+                      break;
+                    }
+                  }
+                  P4EST_ASSERT (j < 4);
+                  dir = j / 2;
+
+                  cid[0] = p8est_face_permutations[fo][0];
+                  cid[1] = p8est_face_permutations[fo][1];
+                  diff = cid[1] - cid[0];
+                  diff = (diff < 0) ? -diff : diff;
+
+                  if (diff == 2) {
+                    dir ^= 1;
+                  }
+
+                  if (!dir) {
+                    /* first direction */
+                    child_edge = he[0];
+                  }
+                  else {
+                    /* second direction */
+                    child_edge = he[1];
+                  }
+
+                  child =
+                    child_offsets[quad_to_local[qid * V + f]] + P4EST_HALF +
+                    child_edge;
+                  quad_to_local[qid * V + v] = child;
+                  referenced[child] = 1;
+                  if (child_edge & 1) {
+                    quad_to_orientations[qid * no + P4EST_FACES + edge] ^= 1;
+                  }
+                  break;
+                }
+              }
+              P4EST_ASSERT (i < 2);
+            }
+          }
+        }
+      }
+#endif
+    }
+  }
+}
+
+/* *INDENT-OFF* */
+#ifndef P4_TO_P8
+static int p4est_to_plex_child_id[1][3] = {{9, 10, 25}};
+static int p4est_to_plex_face_orientation[4][2] = {{-2,  0},
+                                                   { 0, -2},
+                                                   { 0, -2},
+                                                   {-2,  0}};
+static int p4est_to_plex_position[1][4] = {{3, 1, 0, 2}};
+#else
+static int p4est_to_plex_child_id[2][9] =
+                                     {{15, 16, 18, 17, 90, 88, 87, 89, 137},
+                                      {63, 64, 125, -1, -1, -1, -1, -1, -1}};
+static int p4est_to_plex_face_orientation[6][8] =
+                                           {{-4,  0,  3, -1, -3,  1,  2, -2},
+                                            { 0, -4, -1,  3,  1, -3, -2,  2},
+                                            { 0, -4, -1,  3,  1, -3, -2,  2},
+                                            {-1,  1,  0, -2, -4,  2,  3, -3},
+                                            {-4,  0,  3, -1, -3,  1,  2, -2},
+                                            { 0, -4, -1,  3,  1, -3, -2,  2}};
+static int p4est_to_plex_edge_orientation[4][2] = {{-2,  0},
+                                                   { 0, -2},
+                                                   { 0, -2},
+                                                   {-2,  0}};
+static int p4est_to_plex_position[2][6] = {{5, 4, 2, 3, 0, 1},
+                                           {3, 1, 0, 2, -1, -1}};
+#endif
+/* *INDENT-ON* */
+
+static void
+p4est_get_plex_data_int (p4est_t * p4est, p4est_ghost_t * ghost,
+                         p4est_lnodes_t * lnodes, int overlap,
+                         int local_first, p4est_locidx_t * first_local_quad,
+                         sc_array_t * out_points_per_dim,
+                         sc_array_t * out_cone_sizes, sc_array_t * out_cones,
+                         sc_array_t * out_cone_orientations,
+                         sc_array_t * out_vertex_coords,
+                         sc_array_t * out_children, sc_array_t * out_parents,
+                         sc_array_t * out_childids, sc_array_t * out_leaves,
+                         sc_array_t * out_remotes)
+{
+#ifndef P4_TO_P8
+  int                 dim_limits[3] = { 0, 4, 8 };
+  int                 no = P4EST_FACES;
+#else
+  int                 dim_limits[4] = { 0, 6, 18, 26 };
+  int                 no = P4EST_FACES + P8EST_EDGES;
+#endif
+  p4est_locidx_t     *cones;
+  int                *orientations;
+  sc_array_t         *child_to_parent, *child_to_id;
+  p4est_locidx_t     *quad_to_local, *quad_to_local_orig = NULL;
+  int8_t             *quad_to_orientations, *quad_to_orientations_orig = NULL;
+  int8_t             *referenced;
+  p4est_lnodes_code_t *F;
+  p4est_topidx_t      t, flt = p4est->first_local_tree;
+  p4est_topidx_t      llt = p4est->last_local_tree;
+  p4est_locidx_t      il, G = (p4est_locidx_t) ghost->ghosts.elem_count;
+  p4est_locidx_t      Klocal = p4est->local_num_quadrants;
+  p4est_locidx_t      K = Klocal + (overlap ? G : 0);
+  p4est_locidx_t      Gpre, Gpost;
+  int                 p, mpirank = p4est->mpirank;
+  int                 mpisize = p4est->mpisize;
+  p4est_locidx_t      qid;
+  int                 v, V = lnodes->vnodes;
+  sc_array_t         *is_parent, *node_dim;
+  int                 ctype_int = p4est_connect_type_int (P4EST_CONNECT_FULL);
+  p4est_locidx_t      num_global, num_global_plus_children, last_global,
+    *child_offsets;
+  sc_array_t         *all_global;
+  p4est_locidx_t      num_mirrors =
+    (p4est_locidx_t) ghost->mirrors.elem_count;
+
+  P4EST_ASSERT (lnodes->degree == -ctype_int);
+
+  if (overlap) {
+    /* get the face codes for ghosts */
+    p4est_lnodes_code_t **mirror_data_F;
+
+    F = P4EST_ALLOC (p4est_lnodes_code_t, K);
+    memcpy (F, lnodes->face_code, Klocal * sizeof (p4est_lnodes_code_t));
+
+    mirror_data_F = P4EST_ALLOC (p4est_lnodes_code_t *, num_mirrors);
+    for (il = 0; il < num_mirrors; il++) {
+      p4est_quadrant_t   *q;
+
+      q = p4est_quadrant_array_index (&ghost->mirrors, il);
+      qid = q->p.piggy3.local_num;
+      mirror_data_F[il] = &F[qid];
+    }
+    p4est_ghost_exchange_custom (p4est, ghost,
+                                 sizeof (p4est_lnodes_code_t),
+                                 (void **) mirror_data_F, &F[Klocal]);
+    P4EST_FREE (mirror_data_F);
+  }
+  else {
+    F = lnodes->face_code;
+  }
+
+  /* we use the existing lnodes global indices as our anchors to keep all of
+   * the new indices straight */
+  /* create a list of all global nodes seen */
+  {
+    p4est_gloidx_t     *quad_to_global = P4EST_ALLOC (p4est_gloidx_t, K * V);
+
+    /* fill the local portion of quad_to_global */
+    for (qid = 0; qid < Klocal * V; qid++) {
+      p4est_locidx_t      nid;
+
+      nid = lnodes->element_nodes[qid];
+      quad_to_global[qid] = p4est_lnodes_global_index (lnodes, nid);
+    }
+    if (overlap) {
+      /* get the ghost portion of quad_to_global */
+      p4est_gloidx_t    **mirror_data;
+
+      mirror_data = P4EST_ALLOC (p4est_gloidx_t *, num_mirrors);
+      for (il = 0; il < num_mirrors; il++) {
+        p4est_quadrant_t   *q;
+
+        q = p4est_quadrant_array_index (&ghost->mirrors, il);
+        qid = q->p.piggy3.local_num;
+        mirror_data[il] = &quad_to_global[qid * V];
+      }
+      p4est_ghost_exchange_custom (p4est, ghost,
+                                   (size_t) V * sizeof (p4est_gloidx_t),
+                                   (void **) mirror_data,
+                                   &quad_to_global[Klocal * V]);
+      P4EST_FREE (mirror_data);
+    }
+
+    all_global = sc_array_new_size (2 * sizeof (p4est_gloidx_t), V * K);
+    for (qid = 0; qid < K; qid++) {
+      for (v = 0; v < V; v++) {
+        p4est_gloidx_t     *pair =
+          (p4est_gloidx_t *) sc_array_index (all_global,
+                                             (size_t) qid * V + v);
+
+        pair[0] = quad_to_global[qid * V + v];
+        pair[1] = qid * V + v;
+      }
+    }
+    P4EST_FREE (quad_to_global);
+  }
+
+  /* assign a local index to each global node referenced */
+  sc_array_sort (all_global, p4est_gloidx_compare);
+  quad_to_local = P4EST_ALLOC (p4est_locidx_t, K * V);
+  num_global = 0;
+  last_global = -1;
+  for (il = 0; il < K * V; il++) {
+    p4est_gloidx_t     *pair =
+      (p4est_gloidx_t *) sc_array_index (all_global, (size_t) il);
+    p4est_gloidx_t      gidx;
+
+    gidx = pair[0];
+    if (gidx != last_global) {
+      num_global++;
+      last_global = gidx;
+    }
+    quad_to_local[pair[1]] = num_global - 1;
+  }
+
+  if (mpisize > 1) {
+    quad_to_local_orig = P4EST_ALLOC (p4est_locidx_t, K * V);
+    memcpy (quad_to_local_orig, quad_to_local,
+            K * V * sizeof (p4est_locidx_t));
+  }
+  /* remove duplicates from all_global so we can use it to search for the
+   * local index of a global index */
+  sc_array_uniq (all_global, p4est_gloidx_compare);
+  P4EST_ASSERT (all_global->elem_count == (size_t) num_global);
+
+  /* in lnodes, hanging faces/edges/corners are not assigned indices, they are
+   * simply references to the anchor points with a hanging type arrow.  In
+   * plex, however, all points have indices, so we have to expand these points
+   * at the end of the list of local nodes (they do not, however, get global
+   * indices) */
+  /* figure out which nodes are parents and mark node dimensions */
+  is_parent = sc_array_new_size (sizeof (int8_t), num_global);
+  node_dim = sc_array_new_size (sizeof (int8_t), num_global);
+  memset (is_parent->array, 0, is_parent->elem_count * is_parent->elem_size);
+  for (qid = 0; qid < K; qid++) {
+    mark_parent (qid, ctype_int, F, quad_to_local,
+                 (int8_t *) is_parent->array, (int8_t *) node_dim->array);
+  }
+  child_offsets = P4EST_ALLOC (p4est_locidx_t, num_global + 1);
+  /* childredn are appended to the list of global nodes */
+  child_offsets[0] = num_global;
+  for (il = 0; il < num_global; il++) {
+    int8_t              parent =
+      *((int8_t *) sc_array_index (is_parent, (size_t) il));
+    int8_t              dim =
+      *((int8_t *) sc_array_index (node_dim, (size_t) il));
+    int                 count;
+    if (!parent) {
+      count = 0;
+    }
+    else {
+      P4EST_ASSERT (dim == 1 || dim == 2);
+      if (dim == 1) {
+        count = 3;
+      }
+      else {
+        count = 9;
+      }
+    }
+    child_offsets[il + 1] = child_offsets[il] + count;
+  }
+  sc_array_destroy (is_parent);
+  num_global_plus_children = child_offsets[num_global];
+
+  /* expand the node_dim array so that they also have entries
+   * for children */
+  sc_array_resize (node_dim, num_global_plus_children);
+  child_to_id =
+    sc_array_new_size (sizeof (p4est_locidx_t), num_global_plus_children);
+  child_to_parent =
+    sc_array_new_size (sizeof (p4est_locidx_t), num_global_plus_children);
+  memset (child_to_id->array, -1,
+          child_to_parent->elem_count * child_to_parent->elem_size);
+  memset (child_to_parent->array, -1,
+          child_to_parent->elem_count * child_to_parent->elem_size);
+  /* fill the child_to_parent, child_to_id, and node_dim arrays for
+   * the children */
+  for (il = 0; il < num_global; il++) {
+    p4est_locidx_t      jstart, jend, jl;
+    int8_t              pdim =
+      *((int8_t *) sc_array_index (node_dim, (size_t) il));
+
+    jstart = child_offsets[il];
+    jend = child_offsets[il + 1];
+    for (jl = jstart; jl < jend; jl++) {
+      int8_t             *dim =
+        (int8_t *) sc_array_index (node_dim, (size_t) jl);
+      p4est_locidx_t     *parentidx =
+        (p4est_locidx_t *) sc_array_index (child_to_parent, (size_t) jl);
+      p4est_locidx_t     *childid =
+        (p4est_locidx_t *) sc_array_index (child_to_id, (size_t) jl);
+
+      *parentidx = (p4est_locidx_t) il;
+      *childid = (p4est_locidx_t) (jl - jstart);
+      if (pdim == 1) {
+        if (jl - jstart < 2) {
+          *dim = 1;
+        }
+        else {
+          *dim = 0;
+        }
+      }
+      else {
+        if (jl - jstart < 4) {
+          *dim = 2;
+        }
+        else if (jl - jstart < 8) {
+          *dim = 1;
+        }
+        else {
+          *dim = 0;
+        }
+      }
+    }
+  }
+
+  /* loop over quads:
+   * - where quad_to_local refers to a parent, make it refer to the correct
+   *   child
+   * - mark children that are actually referenced (some may not be)
+   * - fill quad_to_orientations
+   */
+  quad_to_orientations = P4EST_ALLOC (int8_t, K * no);
+  if (quad_to_local_orig) {
+    quad_to_orientations_orig = P4EST_ALLOC (int8_t, K * no);
+    memset (quad_to_orientations_orig, -1, K * no * sizeof (int8_t));
+  }
+#ifdef P4EST_ENABLE_DEBUG
+  memset (quad_to_orientations, -1, K * no * sizeof (int8_t));
+#endif
+  referenced = P4EST_ALLOC_ZERO (int8_t, num_global_plus_children);
+  for (il = 0; il < num_global; il++) {
+    referenced[il] = 1;
+  }
+  for (qid = 0, t = flt; t <= llt; t++) {
+    p4est_tree_t       *tree = p4est_tree_array_index (p4est->trees, t);
+    sc_array_t         *quadrants = &(tree->quadrants);
+    p4est_locidx_t      num_quads = (p4est_locidx_t) quadrants->elem_count;
+
+    for (il = 0; il < num_quads; il++, qid++) {
+      p4est_quadrant_t   *q =
+        p4est_quadrant_array_index (quadrants, (size_t) il);
+
+      parent_to_child (q, t, qid, ctype_int, F, quad_to_local,
+                       quad_to_orientations,
+                       quad_to_orientations_orig,
+                       referenced,
+                       (int8_t *) node_dim->array, child_offsets,
+                       (p4est_locidx_t *) child_to_id->array,
+                       p4est->connectivity);
+    }
+  }
+  if (overlap) {
+    for (t = 0; t < p4est->connectivity->num_trees; t++) {
+      p4est_locidx_t      il, istart = ghost->tree_offsets[t];
+      p4est_locidx_t      iend = ghost->tree_offsets[t + 1];
+
+      for (il = istart; il < iend; il++) {
+        p4est_quadrant_t   *q =
+          p4est_quadrant_array_index (&ghost->ghosts, (size_t) il);
+
+        parent_to_child (q, t, il + Klocal, ctype_int, F, quad_to_local,
+                         quad_to_orientations,
+                         quad_to_orientations_orig,
+                         referenced,
+                         (int8_t *) node_dim->array, child_offsets,
+                         (p4est_locidx_t *) child_to_id->array,
+                         p4est->connectivity);
+      }
+    }
+    P4EST_FREE (F);
+  }
+  P4EST_FREE (child_offsets);
+#ifdef P4EST_ENABLE_DEBUG
+  for (il = 0; il < K * no; il++) {
+    P4EST_ASSERT (quad_to_orientations[il] >= 0);
+  }
+#endif
+
+  /* compress unreferenced children out of the local list */
+  {
+    p4est_locidx_t     *old_to_new, *new_to_old;
+    p4est_locidx_t      new_count, diff;
+
+    old_to_new = P4EST_ALLOC (p4est_locidx_t, num_global_plus_children);
+    new_to_old = P4EST_ALLOC (p4est_locidx_t, num_global_plus_children);
+
+    memset (old_to_new, -1, num_global_plus_children * sizeof (*old_to_new));
+    memset (new_to_old, -1, num_global_plus_children * sizeof (*new_to_old));
+    new_count = 0;
+    for (il = 0; il < num_global_plus_children; il++) {
+      if (referenced[il]) {
+        p4est_locidx_t      newidx;
+
+        newidx = new_count++;
+        old_to_new[il] = newidx;
+        new_to_old[newidx] = il;
+      }
+    }
+    P4EST_ASSERT (new_count >= num_global
+                  && new_count <= num_global_plus_children);
+    diff = num_global_plus_children - new_count;
+    num_global_plus_children -= diff;
+    for (il = 0; il < num_global_plus_children; il++) {
+      p4est_locidx_t      oldidx = new_to_old[il];
+      p4est_locidx_t     *pidold, *pidnew, *cidold, *cidnew;
+      int8_t             *dimold, *dimnew;
+
+      if (oldidx != il) {
+        cidold =
+          (p4est_locidx_t *) sc_array_index (child_to_id, (size_t) oldidx);
+        cidnew = (p4est_locidx_t *) sc_array_index (child_to_id, (size_t) il);
+        *cidnew = *cidold;
+        pidold =
+          (p4est_locidx_t *) sc_array_index (child_to_parent,
+                                             (size_t) oldidx);
+        pidnew =
+          (p4est_locidx_t *) sc_array_index (child_to_parent, (size_t) il);
+        *pidnew = *pidold;
+        dimold = (int8_t *) sc_array_index (node_dim, (size_t) oldidx);
+        dimnew = (int8_t *) sc_array_index (node_dim, (size_t) il);
+        *dimnew = *dimold;
+      }
+    }
+    sc_array_resize (child_to_id, num_global_plus_children);
+    sc_array_resize (child_to_parent, num_global_plus_children);
+    sc_array_resize (node_dim, num_global_plus_children);
+    for (il = 0; il < K * V; il++) {
+      quad_to_local[il] = old_to_new[quad_to_local[il]];
+    }
+    P4EST_FREE (old_to_new);
+    P4EST_FREE (new_to_old);
+    P4EST_FREE (referenced);
+  }
+
+  /* now that we have the list of local nodes with children,
+   * we have to assign each local node a plex index: plex indices are
+   * stratified, and include the cells */
+  {
+    int                 c;
+    p4est_locidx_t      dim_counts[4] = { 0 };
+    p4est_locidx_t      dim_offsets[5];
+    p4est_locidx_t      dim_cone_offsets[5];
+    p4est_locidx_t     *plex_to_local;
+    p4est_locidx_t     *local_to_plex;
+    p4est_locidx_t      Nplex = num_global_plus_children + K;
+    int                *plex_to_proc;
+    p4est_locidx_t      point_count;
+    p4est_gloidx_t     *lnode_global_offset;
+    double             *coords;
+
+    /* count the number of points of each dimension */
+    dim_counts[0] = K;
+    for (il = 0; il < num_global_plus_children; il++) {
+      int                 dim =
+        *((int8_t *) sc_array_index (node_dim, (size_t) il));
+
+      P4EST_ASSERT (0 <= dim && dim <= P4EST_DIM - 1);
+      dim_counts[P4EST_DIM - dim]++;
+    }
+    /* get the offsets and the cone offsets of the dimensions */
+    /* fill out_points_per_dim */
+    sc_array_resize (out_points_per_dim, ctype_int + 1);
+    dim_offsets[0] = 0;
+    dim_cone_offsets[0] = 0;
+    for (c = 0; c <= ctype_int; c++) {
+      int                 dim = P4EST_DIM - c;
+      p4est_locidx_t     *ppd =
+        (p4est_locidx_t *) sc_array_index (out_points_per_dim, dim);
+
+      *ppd = dim_counts[c];
+      dim_offsets[c + 1] = dim_offsets[c] + dim_counts[c];
+      dim_cone_offsets[c + 1] = dim_cone_offsets[c] + 2 * dim * dim_counts[c];
+    }
+    P4EST_ASSERT (dim_offsets[ctype_int + 1] == Nplex);
+    for (c = 0; c <= ctype_int; c++) {
+      int                 dim = P4EST_DIM - c;
+
+      dim_offsets[c + 1] = dim_offsets[c] + dim_counts[c];
+      dim_cone_offsets[c + 1] = dim_cone_offsets[c] + 2 * dim * dim_counts[c];
+    }
+    /* fill out_cone_sizes */
+    sc_array_resize (out_cone_sizes, Nplex);
+    for (c = 0; c <= ctype_int; c++) {
+      p4est_locidx_t      pstart, pend;
+      int                 dim = P4EST_DIM - c;
+
+      pstart = dim_offsets[c];
+      pend = dim_offsets[c + 1];
+      for (il = pstart; il < pend; il++) {
+        p4est_locidx_t     *size =
+          (p4est_locidx_t *) sc_array_index (out_cone_sizes, (size_t) il);
+
+        *size = 2 * dim;
+      }
+    }
+    /* construct the local_to_plex, plex_to_local, and plex_to_proc arrays */
+    plex_to_local = P4EST_ALLOC (p4est_locidx_t, Nplex);
+    local_to_plex = P4EST_ALLOC (p4est_locidx_t, Nplex);
+    plex_to_proc = P4EST_ALLOC (int, Nplex);
+    sc_array_resize (out_cones, dim_cone_offsets[ctype_int + 1]);
+    cones = (p4est_locidx_t *) out_cones->array;
+    sc_array_resize (out_cone_orientations, dim_cone_offsets[ctype_int + 1]);
+    orientations = (p4est_locidx_t *) out_cone_orientations->array;
+    sc_array_resize (out_vertex_coords,
+                     dim_offsets[ctype_int + 1] - dim_offsets[ctype_int]);
+    coords = (double *) out_vertex_coords->array;
+#ifdef P4EST_ENABLE_DEBUG
+    memset (plex_to_local, -1, Nplex * sizeof (p4est_locidx_t));
+    memset (local_to_plex, -1, Nplex * sizeof (p4est_locidx_t));
+    memset (plex_to_proc, -1, Nplex * sizeof (int));
+    memset (out_cones->array, -1,
+            out_cones->elem_count * out_cones->elem_size);
+    memset (out_cone_orientations->array, -1,
+            out_cone_orientations->elem_count *
+            out_cone_orientations->elem_size);
+#endif
+    point_count = 0;
+    /* figure out the locations of ghosts within the base */
+    Gpre = (overlap && local_first) ? 0 : (ghost->proc_offsets[mpirank] -
+                                           ghost->proc_offsets[0]);
+    Gpost = overlap ?
+      (local_first ? G : (ghost->proc_offsets[mpisize] -
+                          ghost->proc_offsets[mpirank + 1])) : 0;
+    for (qid = 0, p = 0; qid < Gpre; qid++) {
+      while (p < mpisize && ghost->proc_offsets[p + 1] <= qid) {
+        p++;
+      }
+      P4EST_ASSERT (ghost->proc_offsets[p] <= qid &&
+                    ghost->proc_offsets[p + 1] > qid);
+      il = Klocal + qid;
+      plex_to_local[point_count] = il;
+      plex_to_proc[point_count] = p;
+      local_to_plex[il] = point_count++;
+    }
+    for (il = 0; il < Klocal; il++) {
+      plex_to_local[point_count] = il;
+      plex_to_proc[point_count] = mpirank;
+      local_to_plex[il] = point_count++;
+    }
+    for (qid = 0, p = mpirank + 1; qid < Gpost; qid++) {
+      while (p < mpisize && ghost->proc_offsets[p + 1] <= qid + Gpre) {
+        p++;
+      }
+      P4EST_ASSERT (ghost->proc_offsets[p] <= qid + Gpre &&
+                    ghost->proc_offsets[p + 1] > qid + Gpre);
+      il = Klocal + qid + Gpre;
+      plex_to_local[point_count] = il;
+      plex_to_proc[point_count] = p;
+      local_to_plex[il] = point_count++;
+    }
+    /* reset dim counts so that we can us them for the offsets */
+    for (c = 1; c <= ctype_int; c++) {
+      dim_counts[c] = 0;
+    }
+    /* compute the lnodes partition */
+    lnode_global_offset = P4EST_ALLOC (p4est_gloidx_t, mpisize + 1);
+    lnode_global_offset[0] = 0;
+    for (p = 0; p < mpisize; p++) {
+      lnode_global_offset[p + 1] =
+        lnode_global_offset[p] + lnodes->global_owned_count[p];
+    }
+    for (il = 0, p = 0; il < num_global_plus_children; il++) {
+      int                 dim =
+        *((int8_t *) sc_array_index (node_dim, (size_t) il));
+      int                 offset;
+      p4est_locidx_t      pid, nid;
+      p4est_gloidx_t      gid = -1;
+
+      P4EST_ASSERT (0 <= dim && dim <= P4EST_DIM - 1);
+      if (il < num_global) {
+        gid = *((p4est_gloidx_t *) sc_array_index (all_global, il));
+        while (p < mpisize && lnode_global_offset[p + 1] <= gid) {
+          p++;
+        }
+        P4EST_ASSERT (lnode_global_offset[p] <= gid &&
+                      gid < lnode_global_offset[p + 1]);
+      }
+      c = P4EST_DIM - dim;
+      offset = dim_counts[c]++;
+      pid = dim_offsets[c] + offset;
+      nid = il + K;
+      plex_to_local[pid] = nid;
+      local_to_plex[nid] = pid;
+      if (gid >= 0) {
+        plex_to_proc[pid] = p;
+      }
+      else {
+        plex_to_proc[pid] = mpirank;
+      }
+    }
+    P4EST_FREE (lnode_global_offset);
+#ifdef P4EST_ENABLE_DEBUG
+    for (il = 0; il < Nplex; il++) {
+      P4EST_ASSERT (plex_to_local[il] >= 0);
+      P4EST_ASSERT (local_to_plex[il] >= 0);
+      P4EST_ASSERT (plex_to_proc[il] >= 0);
+    }
+#endif
+    /* now that we have the plex indices, we can compute out_children,
+     * out_parents, out_childids */
+    for (il = 0; il < Nplex; il++) {
+      p4est_locidx_t      nid = plex_to_local[il] - K;
+      p4est_locidx_t      parent;
+
+      if (nid < 0) {
+        continue;
+      }
+      parent = *((p4est_locidx_t *) sc_array_index (child_to_parent, nid));
+      if (parent >= 0) {
+        p4est_locidx_t     *outc =
+          (p4est_locidx_t *) sc_array_push (out_children);
+        p4est_locidx_t     *outp =
+          (p4est_locidx_t *) sc_array_push (out_parents);
+        p4est_locidx_t     *outid =
+          (p4est_locidx_t *) sc_array_push (out_childids);
+        int8_t              pdim =
+          *((int8_t *) sc_array_index (node_dim, (size_t) parent));
+        p4est_locidx_t      id;
+
+        *outc = il;
+        *outp = local_to_plex[parent + K];
+        id = *((p4est_locidx_t *) sc_array_index (child_to_id, nid));
+        *outid = p4est_to_plex_child_id[P4EST_DIM - 1 - pdim][id];
+      }
+    }
+    sc_array_destroy (child_to_parent);
+    sc_array_destroy (child_to_id);
+
+    /* compute cones and orientations */
+    for (qid = 0; qid < K; qid++) {
+      il = plex_to_local[qid];
+      for (c = 0; c < ctype_int; c++) {
+        int                 v, vstart, vend;
+        int                 dim = P4EST_DIM - 1 - c;
+
+        vstart = dim_limits[c];
+        vend = dim_limits[c + 1];
+        if (!c) {
+          /* cell to face cones */
+          p4est_locidx_t      pid;
+          p4est_locidx_t      cone_off;
+
+          pid = qid;
+          cone_off = P4EST_FACES * pid;
+          for (v = vstart; v < vend; v++) {
+            int                 pos;
+
+            pos = p4est_to_plex_position[0][v - vstart];
+            P4EST_ASSERT (cones[cone_off + pos] == -1);
+
+            cones[cone_off + pos] =
+              local_to_plex[quad_to_local[il * V + v] + K];
+            orientations[cone_off + pos] =
+              p4est_to_plex_face_orientation[v][quad_to_orientations
+                                                [il * no + v]];
+          }
+        }
+        else if (c == P4EST_DIM - 1) {
+          /* x to vertex cones */
+          for (v = vstart; v < vend; v++) {
+            int                 corner = v - vstart;
+            int                 j;
+            p4est_locidx_t      vid;
+            int                 iter, limit =
+              (quad_to_local_orig != NULL) ? 2 : 1;
+
+            for (iter = 0; iter < limit; iter++) {
+              p4est_locidx_t     *qtl =
+                (iter == 0) ? quad_to_local : quad_to_local_orig;
+              int8_t             *qto =
+                (iter ==
+                 0) ? quad_to_orientations : quad_to_orientations_orig;
+
+              vid = local_to_plex[qtl[il * V + v] + K];
+              for (j = 0; j < P4EST_DIM; j++) {
+                p4est_locidx_t      pid;
+                p4est_locidx_t      cone_off;
+                int                 k, pos, o;
+
+#ifndef P4_TO_P8
+                k = p4est_corner_faces[corner][j];
+                o = qto[il * no + k];
+                pos = p4est_corner_face_corners[corner][k];
+                P4EST_ASSERT (pos >= 0);
+                pid = local_to_plex[qtl[il * V + k] + K];
+#else
+                k = p8est_corner_edges[corner][j];
+                o = qto[il * no + P4EST_FACES + k];
+                if (p8est_edge_corners[k][0] == corner) {
+                  pos = 0;
+                }
+                else {
+                  pos = 1;
+                }
+                pid = local_to_plex[qtl[il * V + P4EST_FACES + k] + K];
+#endif
+                if (o < 0) {
+                  continue;
+                }
+                if (o) {
+                  pos = (pos ^ 1);
+                }
+                P4EST_ASSERT (pid >= dim_offsets[c]
+                              && pid < dim_offsets[c + 1]);
+                cone_off =
+                  2 * (dim + 1) * (pid - dim_offsets[c]) +
+                  dim_cone_offsets[c];
+                cone_off += pos;
+                /* another cell may have already computed this cell, but we want
+                 * to make sure they agree */
+                P4EST_ASSERT (cones[cone_off] == -1 ||
+                              cones[cone_off] == vid);
+                cones[cone_off] = vid;
+                orientations[cone_off] = 0;
+              }
+            }
+          }
+        }
+#ifdef P4_TO_P8
+        else {
+          /* compute face to edge cones */
+          for (v = vstart; v < vend; v++) {
+            int                 edge = v - vstart;
+            int                 j, o;
+            p4est_locidx_t      vid;
+            int                 iter, limit =
+              (quad_to_local_orig != NULL) ? 2 : 1;
+
+            for (iter = 0; iter < limit; iter++) {
+              p4est_locidx_t     *qtl =
+                (iter == 0) ? quad_to_local : quad_to_local_orig;
+              int8_t             *qto =
+                (iter ==
+                 0) ? quad_to_orientations : quad_to_orientations_orig;
+
+              vid = local_to_plex[qtl[il * V + v] + K];
+              o = qto[il * no + P4EST_FACES + edge];
+              if (o < 0) {
+                continue;
+              }
+              for (j = 0; j < 2; j++) {
+                p4est_locidx_t      pid;
+                p4est_locidx_t      cone_off;
+                int                 k, f, pos, fo, cid[2], l, minc, maxc;
+                int                 edgeor, faceor;
+
+                k = p8est_edge_faces[edge][j];
+                fo = qto[il * no + k];
+                if (fo < 0) {
+                  continue;
+                }
+                pid = local_to_plex[qtl[il * V + k] + K];
+                for (l = 0; l < 2; l++) {
+                  int                 cor, face_ex, face_in;
+
+                  cor = p8est_edge_corners[edge][l ^ o];
+                  face_ex = p8est_corner_face_corners[cor][k];
+                  P4EST_ASSERT (face_ex >= 0);
+                  face_in = p8est_face_permutations[fo][face_ex];
+                  cid[l] = face_in;
+                }
+                minc = SC_MIN (cid[0], cid[1]);
+                maxc = SC_MAX (cid[0], cid[1]);
+                /* p4est convention is to number x edges before y edges before z
+                 * edges, but to be consistent across dimensions, we treat edges
+                 * as faces, so the order of the dimensions is reversed */
+                if ((maxc - minc) == 2) {
+                  f = (minc & 1);
+                }
+                else {
+                  f = 2 + ((minc & 2) >> 1);
+                }
+                pos = p4est_to_plex_position[1][f];
+                P4EST_ASSERT (pid >= dim_offsets[c]
+                              && pid < dim_offsets[c + 1]);
+                cone_off =
+                  2 * (dim + 1) * (pid - dim_offsets[c]) +
+                  dim_cone_offsets[c];
+                cone_off += pos;
+                P4EST_ASSERT (cones[cone_off] == -1 ||
+                              cones[cone_off] == vid);
+                cones[cone_off] = vid;
+                faceor = cid[0] < cid[1] ? 0 : 1;
+                edgeor = p4est_to_plex_edge_orientation[f][faceor];
+                P4EST_ASSERT (orientations[cone_off] == -1 ||
+                              orientations[cone_off] == edgeor);
+                orientations[cone_off] = edgeor;
+              }
+            }
+          }
+        }
+#endif
+      }
+    }
+#ifdef P4EST_ENABLE_DEBUG
+    {
+      size_t              zz, count = out_cones->elem_count;
+
+      for (zz = 0; zz < count; zz++) {
+        p4est_locidx_t      cone =
+          *((p4est_locidx_t *) sc_array_index (out_cones, zz));
+
+        P4EST_ASSERT (cone >= 0);
+      }
+    }
+#endif
+    /* compute coordinates */
+    for (qid = 0, t = flt; t <= llt; t++) {
+      p4est_tree_t       *tree = p4est_tree_array_index (p4est->trees, t);
+      sc_array_t         *quadrants = &(tree->quadrants);
+      p4est_locidx_t      num_quads = (p4est_locidx_t) quadrants->elem_count;
+
+      for (il = 0; il < num_quads; il++, qid++) {
+        p4est_quadrant_t   *q =
+          p4est_quadrant_array_index (quadrants, (size_t) il);
+        p4est_qcoord_t      h = P4EST_QUADRANT_LEN (q->level);
+        int                 vstart, vend;
+
+        vstart = dim_limits[P4EST_DIM - 1];
+        vend = dim_limits[P4EST_DIM];
+        for (v = vstart; v < vend; v++) {
+          int                 corner = v - vstart;
+          p4est_locidx_t      vid =
+            local_to_plex[quad_to_local[qid * V + v] + K] -
+            dim_offsets[P4EST_DIM];
+          p4est_locidx_t      vid2 =
+            (quad_to_local_orig !=
+             NULL) ? (local_to_plex[quad_to_local_orig[qid * V + v] + K] -
+                      dim_offsets[P4EST_DIM]) : vid;
+          double              vcoord[3];
+
+          p4est_qcoord_to_vertex (p4est->connectivity, t,
+                                  q->x + ((corner & 1) ? h : 0),
+                                  q->y + ((corner & 2) ? h : 0),
+#ifdef P4_TO_P8
+                                  q->z + ((corner & 4) ? h : 0),
+#endif
+                                  vcoord);
+          coords[3 * vid + 0] = vcoord[0];
+          coords[3 * vid + 1] = vcoord[1];
+          coords[3 * vid + 2] = vcoord[2];
+
+          if (vid2 != vid) {
+            p4est_quadrant_t    p;
+
+            p4est_quadrant_parent (q, &p);
+            p4est_qcoord_to_vertex (p4est->connectivity, t,
+                                    p.x + ((corner & 1) ? (2 * h) : 0),
+                                    p.y + ((corner & 2) ? (2 * h) : 0),
+#ifdef P4_TO_P8
+                                    p.z + ((corner & 4) ? (2 * h) : 0),
+#endif
+                                    vcoord);
+            coords[3 * vid2 + 0] = vcoord[0];
+            coords[3 * vid2 + 1] = vcoord[1];
+            coords[3 * vid2 + 2] = vcoord[2];
+          }
+        }
+      }
+    }
+    if (overlap) {
+      for (t = 0; t < p4est->connectivity->num_trees; t++) {
+        p4est_locidx_t      il, istart = ghost->tree_offsets[t];
+        p4est_locidx_t      iend = ghost->tree_offsets[t + 1];
+
+        for (il = istart; il < iend; il++, qid++) {
+          p4est_quadrant_t   *q =
+            p4est_quadrant_array_index (&ghost->ghosts, (size_t) il);
+          p4est_qcoord_t      h = P4EST_QUADRANT_LEN (q->level);
+          int                 vstart, vend;
+
+          vstart = dim_limits[P4EST_DIM - 1];
+          vend = dim_limits[P4EST_DIM];
+          for (v = vstart; v < vend; v++) {
+            int                 corner = v - vstart;
+            p4est_locidx_t      vid =
+              local_to_plex[quad_to_local[qid * V + v] + K] -
+              dim_offsets[P4EST_DIM];
+            p4est_locidx_t      vid2 =
+              (quad_to_local_orig !=
+               NULL) ? (local_to_plex[quad_to_local_orig[qid * V + v] + K] -
+                        dim_offsets[P4EST_DIM]) : vid;
+            double              vcoord[3];
+
+            p4est_qcoord_to_vertex (p4est->connectivity, t,
+                                    q->x + ((corner & 1) ? h : 0),
+                                    q->y + ((corner & 2) ? h : 0),
+#ifdef P4_TO_P8
+                                    q->z + ((corner & 4) ? h : 0),
+#endif
+                                    vcoord);
+            coords[3 * vid + 0] = vcoord[0];
+            coords[3 * vid + 1] = vcoord[1];
+            coords[3 * vid + 2] = vcoord[2];
+
+            if (vid2 != vid) {
+              p4est_quadrant_t    p;
+
+              p4est_quadrant_parent (q, &p);
+              p4est_qcoord_to_vertex (p4est->connectivity, t,
+                                      p.x + ((corner & 1) ? (2 * h) : 0),
+                                      p.y + ((corner & 2) ? (2 * h) : 0),
+#ifdef P4_TO_P8
+                                      p.z + ((corner & 4) ? (2 * h) : 0),
+#endif
+                                      vcoord);
+              coords[3 * vid2 + 0] = vcoord[0];
+              coords[3 * vid2 + 1] = vcoord[1];
+              coords[3 * vid2 + 2] = vcoord[2];
+            }
+          }
+        }
+      }
+    }
+
+    /* cleanup */
+    P4EST_FREE (quad_to_local);
+    P4EST_FREE (quad_to_orientations);
+    if (quad_to_local_orig) {
+      P4EST_FREE (quad_to_local_orig);
+      P4EST_FREE (quad_to_orientations_orig);
+    }
+    sc_array_destroy (node_dim);
+
+    {
+      sc_array_t         *quad_to_plex;
+      sc_array_t         *lnodes_to_plex;
+
+      /* communicate local_to_plex for quads to compute leaves and remotes */
+      if (overlap) {
+        p4est_locidx_t    **mirror_data;
+        sc_array_t         *quad_plex;
+
+        mirror_data = P4EST_ALLOC (p4est_locidx_t *, num_mirrors);
+        quad_plex = sc_array_new_size (sizeof (p4est_locidx_t), K);
+        for (il = 0; il < Klocal; il++) {
+          p4est_locidx_t     *ql =
+            (p4est_locidx_t *) sc_array_index (quad_plex, il);
+          *ql = local_to_plex[il];
+        }
+        for (il = 0; il < num_mirrors; il++) {
+          p4est_quadrant_t   *q;
+
+          q = p4est_quadrant_array_index (&ghost->mirrors, il);
+          qid = q->p.piggy3.local_num;
+          mirror_data[il] =
+            (p4est_locidx_t *) sc_array_index (quad_plex, qid);
+        }
+        p4est_ghost_exchange_custom (p4est, ghost,
+                                     (size_t) sizeof (p4est_locidx_t),
+                                     (void **) mirror_data, (p4est_locidx_t *)
+                                     sc_array_index (quad_plex, Klocal));
+        P4EST_FREE (mirror_data);
+        for (il = 0; il < K; il++) {
+          p = plex_to_proc[il];
+          if (p != mpirank) {
+            p4est_locidx_t      ql;
+            p4est_locidx_t     *leaf =
+              (p4est_locidx_t *) sc_array_push (out_leaves);
+            p4est_locidx_t     *remote =
+              (p4est_locidx_t *) sc_array_push (out_remotes);
+
+            qid = plex_to_local[il];
+            ql = *((p4est_locidx_t *) sc_array_index (quad_plex, qid));
+            *leaf = il;
+            remote[0] = p;
+            remote[1] = ql;
+          }
+        }
+        sc_array_destroy (quad_plex);
+      }
+      /* communicate all_global * local_to_plex to build leaves and remotes */
+      lnodes_to_plex =
+        sc_array_new_size (sizeof (p4est_locidx_t), lnodes->num_local_nodes);
+      quad_to_plex = sc_array_new_size (sizeof (p4est_locidx_t), V * K);
+      if (lnodes->owned_count) {
+        ssize_t             firstidx;
+
+        firstidx =
+          sc_array_bsearch (all_global, &lnodes->global_offset,
+                            p4est_gloidx_compare);
+        P4EST_ASSERT (firstidx >= 0);
+        for (il = 0; il < lnodes->owned_count; il++) {
+          p4est_locidx_t     *lp =
+            (p4est_locidx_t *) sc_array_index (lnodes_to_plex, (size_t) il);
+
+          *lp = local_to_plex[firstidx + il + K];
+        }
+      }
+      p4est_lnodes_share_owned (lnodes_to_plex, lnodes);
+      for (il = 0; il < Klocal; il++) {
+        for (v = 0; v < V; v++) {
+          p4est_locidx_t      nid = lnodes->element_nodes[il * V + v];
+          p4est_locidx_t      lp = *((p4est_locidx_t *)
+                                     sc_array_index (lnodes_to_plex,
+                                                     (size_t) nid));
+          p4est_locidx_t     *qp =
+            (p4est_locidx_t *) sc_array_index (quad_to_plex,
+                                               (size_t) (il * V + v));
+
+          *qp = lp;
+        }
+      }
+      sc_array_destroy (lnodes_to_plex);
+      if (overlap) {
+        p4est_locidx_t    **mirror_data;
+
+        mirror_data = P4EST_ALLOC (p4est_locidx_t *, num_mirrors);
+        for (il = 0; il < num_mirrors; il++) {
+          p4est_quadrant_t   *q;
+
+          q = p4est_quadrant_array_index (&ghost->mirrors, il);
+          qid = q->p.piggy3.local_num;
+          mirror_data[il] =
+            (p4est_locidx_t *) sc_array_index (quad_to_plex, qid * V);
+        }
+        p4est_ghost_exchange_custom (p4est, ghost,
+                                     (size_t) V * sizeof (p4est_locidx_t),
+                                     (void **) mirror_data, (p4est_locidx_t *)
+                                     sc_array_index (quad_to_plex,
+                                                     Klocal * V));
+        P4EST_FREE (mirror_data);
+      }
+      for (il = 0; il < num_global; il++) {
+        p4est_locidx_t      localpid = il + K;
+
+        p = plex_to_proc[localpid];
+        if (p != mpirank) {
+          p4est_gloidx_t     *gid =
+            (p4est_gloidx_t *) sc_array_index (all_global, il);
+          p4est_locidx_t      nid = gid[1];
+          p4est_locidx_t      pid =
+            *((p4est_locidx_t *) sc_array_index (quad_to_plex, (size_t) nid));
+          p4est_locidx_t     *leaf =
+            (p4est_locidx_t *) sc_array_push (out_leaves);
+          p4est_locidx_t     *remote =
+            (p4est_locidx_t *) sc_array_push (out_remotes);
+
+          *leaf = localpid;
+          remote[0] = p;
+          remote[1] = pid;
+        }
+      }
+      sc_array_destroy (quad_to_plex);
+    }
+    P4EST_FREE (plex_to_local);
+    P4EST_FREE (local_to_plex);
+    P4EST_FREE (plex_to_proc);
+  }
+
+  /* cleanup */
+  sc_array_destroy (all_global);
+}
+
+void
+p4est_get_plex_data (p4est_t * p4est, p4est_connect_type_t ctype,
+                     int overlap,
+                     p4est_locidx_t * first_local_quad,
+                     sc_array_t * out_points_per_dim,
+                     sc_array_t * out_cone_sizes,
+                     sc_array_t * out_cones,
+                     sc_array_t * out_cone_orientations,
+                     sc_array_t * out_vertex_coords,
+                     sc_array_t * out_children,
+                     sc_array_t * out_parents,
+                     sc_array_t * out_childids,
+                     sc_array_t * out_leaves, sc_array_t * out_remotes)
+{
+  p4est_ghost_t      *ghost;
+  p4est_lnodes_t     *lnodes;
+  int                 ctype_int = p4est_connect_type_int (ctype);
+  int                 i;
+
+  ghost = p4est_ghost_new (p4est, ctype);
+  lnodes = p4est_lnodes_new (p4est, ghost, -ctype_int);
+  if (overlap) {
+    p4est_ghost_support_lnodes (p4est, lnodes, ghost);
+  }
+  for (i = 1; i < overlap; i++) {
+    p4est_ghost_expand_by_lnodes (p4est, lnodes, ghost);
+  }
+  if (ctype != P4EST_CONNECT_FULL) {
+    p4est_lnodes_destroy (lnodes);
+    lnodes = p4est_lnodes_new (p4est, ghost, -ctype);
+  }
+  p4est_get_plex_data_int (p4est, ghost, lnodes, overlap, 0,
+                           first_local_quad, out_points_per_dim,
+                           out_cone_sizes, out_cones, out_cone_orientations,
+                           out_vertex_coords, out_children, out_parents,
+                           out_childids, out_leaves, out_remotes);
+  p4est_ghost_destroy (ghost);
+  p4est_lnodes_destroy (lnodes);
+}
diff --git a/src/p4est_plex.h b/src/p4est_plex.h
new file mode 100644
index 0000000..397342c
--- /dev/null
+++ b/src/p4est_plex.h
@@ -0,0 +1,83 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_PLEX_H
+#define P4EST_PLEX_H
+
+#include <p4est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Create the data necessary to create a PETsc DMPLEX representation of a
+ * forest.  The forest must be at least face balanced (see p4est_balance()).
+ * See test/test_plex2.c for example usage.
+ *
+ * All arrays should be initialized to hold sizeof (p4est_locidx_t), except
+ * for \a out_remotes, which should be initialized to hold
+ * (2 * sizeof (p4est_locidx_t)).
+ *
+ * \param[in]     p4est                 the forest
+ * \param[in]     ctype                 the type of adjacency for the overlap
+ * \param[in]     overlap               the number of layers of overlap (zero
+ *                                      is acceptable)
+ * \param[out]    first_local_quad      the local quadrants are assigned
+ *                                      contiguous plex indices, starting with
+ *                                      this index
+ * \param[in,out] out_points_per_dim    filled with argument for
+ *                                      DMPlexCreateFromDAG()
+ * \param[in,out] out_cone_sizes        filled with argument for
+ *                                      DMPlexCreateFromDAG()
+ * \param[in,out] out_cones             filled with argument for
+ *                                      DMPlexCreateFromDAG()
+ * \param[in,out] out_cone_orientations filled with argument for
+ *                                      DMPlexCreateFromDAG()
+ * \param[in,out] out_vertex_coords     filled with argument for
+ *                                      DMPlexCreateFromDAG()
+ * \param[in,out] out_children          filled with argument for
+ *                                      DMPlexSetTree()
+ * \param[in,out] out_parents           filled with argument for
+ *                                      DMPlexSetTree()
+ * \param[in,out] out_childids          filled with argument for
+ *                                      DMPlexSetTree()
+ * \param[in,out] out_leaves            filled with argument for
+ *                                      PetscSFSetGraph()
+ * \param[in,out] out_remotes           filled with argument for
+ *                                      PetscSFSetGraph()
+ */
+void                p4est_get_plex_data (p4est_t * p4est,
+                                         p4est_connect_type_t ctype,
+                                         int overlap,
+                                         p4est_locidx_t * first_local_quad,
+                                         sc_array_t * out_points_per_dim,
+                                         sc_array_t * out_cone_sizes,
+                                         sc_array_t * out_cones,
+                                         sc_array_t * out_cone_orientations,
+                                         sc_array_t * out_vertex_coords,
+                                         sc_array_t * out_children,
+                                         sc_array_t * out_parents,
+                                         sc_array_t * out_childids,
+                                         sc_array_t * out_leaves,
+                                         sc_array_t * out_remotes);
+
+SC_EXTERN_C_END;
+#endif /* P4EST_PLEX_H */
diff --git a/src/p4est_points.c b/src/p4est_points.c
new file mode 100644
index 0000000..88313d7
--- /dev/null
+++ b/src/p4est_points.c
@@ -0,0 +1,394 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/********************************************************************
+ *                          IMPORTANT NOTE                          *
+ *                                                                  *
+ * The p4est_points functionality depends on sc/src/sc_sort.        *
+ * That parallel bitonic sort is still buggy (see sc/bugs).         *
+ * If you want to use this code you have to fix the sort first.     *
+ ********************************************************************/
+
+#ifdef P4_TO_P8
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_communication.h>
+#include <p8est_extended.h>
+#include <p8est_points.h>
+#else
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_communication.h>
+#include <p4est_extended.h>
+#include <p4est_points.h>
+#endif /* !P4_TO_P8 */
+#include <sc_allgather.h>
+#include <sc_sort.h>
+
+typedef struct
+{
+  p4est_quadrant_t   *points;
+  p4est_locidx_t      num_points, max_points, current;
+  int                 maxlevel;
+}
+p4est_points_state_t;
+
+static void
+p4est_points_init (p4est_t * p4est, p4est_topidx_t which_tree,
+                   p4est_quadrant_t * quadrant)
+{
+  p4est_points_state_t *s = (p4est_points_state_t *) p4est->user_pointer;
+  p4est_locidx_t     *qdata = (p4est_locidx_t *) quadrant->p.user_data;
+  p4est_quadrant_t   *p;
+
+  qdata[0] = s->current;
+  while (s->current < s->num_points) {
+    p = s->points + s->current;
+    P4EST_ASSERT (p->p.which_tree >= which_tree);
+    if (p->p.which_tree > which_tree) {
+      break;
+    }
+    P4EST_ASSERT (p4est_quadrant_is_node (p, 1));
+    P4EST_ASSERT (p4est_quadrant_compare (quadrant, p) < 0);
+    if (!p4est_quadrant_contains_node (quadrant, p)) {
+      break;
+    }
+    ++s->current;
+  }
+  qdata[1] = s->current;
+}
+
+static int
+p4est_points_refine (p4est_t * p4est, p4est_topidx_t which_tree,
+                     p4est_quadrant_t * quadrant)
+{
+  p4est_points_state_t *s = (p4est_points_state_t *) p4est->user_pointer;
+  p4est_locidx_t     *qdata = (p4est_locidx_t *) quadrant->p.user_data;
+
+  P4EST_ASSERT (s->max_points >= 0);
+  if (qdata[1] > qdata[0] + s->max_points) {
+    s->current = qdata[0];
+    return 1;
+  }
+  else {
+    return 0;
+  }
+}
+
+p4est_t            *
+p4est_new_points (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity,
+                  int maxlevel, p4est_quadrant_t * points,
+                  p4est_locidx_t num_points, p4est_locidx_t max_points,
+                  size_t data_size, p4est_init_t init_fn, void *user_pointer)
+{
+  int                 mpiret;
+  int                 num_procs, rank;
+  int                 i, isizet;
+  size_t              lcount;
+  size_t             *nmemb;
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              zz;
+#endif
+  p4est_topidx_t      jt, num_trees;
+  p4est_topidx_t      first_tree, last_tree, next_tree;
+  p4est_quadrant_t   *first_quad, *next_quad, *quad;
+  p4est_quadrant_t    a, b, c, f, l, n;
+  p4est_tree_t       *tree;
+  p4est_t            *p4est;
+  p4est_points_state_t ppstate;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING
+                            "_new_points with max level %d max points %lld\n",
+                            maxlevel, (long long) max_points);
+  p4est_log_indent_push ();
+  P4EST_ASSERT (p4est_connectivity_is_valid (connectivity));
+  P4EST_ASSERT (max_points >= -1);
+
+  /* retrieve MPI information */
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  /* This implementation runs in O(P/p * maxlevel)
+   * with P the total number of points, p the number of processors.
+   * Two optimizations are possible:
+   * 1. At startup remove points that lead to duplicate quadrants.
+   * 2. Use complete_region between successive points instead of
+   *    the call to refine. This should give O(N/p) * maxlevel
+   *    with N the total number of quadrants.
+   */
+
+  /* parallel sort the incoming points */
+  lcount = (size_t) num_points;
+  nmemb = P4EST_ALLOC_ZERO (size_t, num_procs);
+  isizet = (int) sizeof (size_t);
+  mpiret = sc_MPI_Allgather (&lcount, isizet, sc_MPI_BYTE,
+                             nmemb, isizet, sc_MPI_BYTE, mpicomm);
+  SC_CHECK_MPI (mpiret);
+  sc_psort (mpicomm, points, nmemb, sizeof (p4est_quadrant_t),
+            p4est_quadrant_compare_piggy);
+  P4EST_FREE (nmemb);
+#ifdef P4EST_ENABLE_DEBUG
+  first_quad = points;
+  for (zz = 1; zz < lcount; ++zz) {
+    next_quad = points + zz;
+    P4EST_ASSERT (p4est_quadrant_compare_piggy (first_quad, next_quad) <= 0);
+    first_quad = next_quad;
+  }
+#endif
+
+  /* create the p4est */
+  p4est = P4EST_ALLOC_ZERO (p4est_t, 1);
+  ppstate.points = points;
+  ppstate.num_points = num_points;
+  ppstate.max_points = max_points;
+  ppstate.current = 0;
+  ppstate.maxlevel = maxlevel;
+
+  /* assign some data members */
+  p4est->mpicomm = mpicomm;
+  p4est->mpisize = num_procs;
+  p4est->mpirank = rank;
+  p4est->data_size = 2 * sizeof (p4est_locidx_t);       /* temporary */
+  p4est->user_pointer = &ppstate;
+  p4est->connectivity = connectivity;
+  num_trees = connectivity->num_trees;
+
+  /* allocate memory pools */
+  p4est->user_data_pool = sc_mempool_new (p4est->data_size);
+  p4est->quadrant_pool = sc_mempool_new (sizeof (p4est_quadrant_t));
+
+  P4EST_GLOBAL_PRODUCTIONF ("New " P4EST_STRING
+                            " with %lld trees on %d processors\n",
+                            (long long) num_trees, num_procs);
+
+  /* allocate trees */
+  p4est->trees = sc_array_new (sizeof (p4est_tree_t));
+  sc_array_resize (p4est->trees, num_trees);
+  for (jt = 0; jt < num_trees; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    sc_array_init (&tree->quadrants, sizeof (p4est_quadrant_t));
+    P4EST_QUADRANT_INIT (&tree->first_desc);
+    P4EST_QUADRANT_INIT (&tree->last_desc);
+    tree->quadrants_offset = 0;
+    for (i = 0; i <= P4EST_QMAXLEVEL; ++i) {
+      tree->quadrants_per_level[i] = 0;
+    }
+    for (; i <= P4EST_MAXLEVEL; ++i) {
+      tree->quadrants_per_level[i] = -1;
+    }
+    tree->maxlevel = 0;
+  }
+  p4est->local_num_quadrants = 0;
+  p4est->global_num_quadrants = 0;
+
+  /* create point based partition */
+  P4EST_QUADRANT_INIT (&f);
+  p4est->global_first_position =
+    P4EST_ALLOC_ZERO (p4est_quadrant_t, num_procs + 1);
+  if (num_points == 0) {
+    P4EST_VERBOSE ("Empty processor");
+    first_tree = p4est->first_local_tree = -1;
+    first_quad = NULL;
+  }
+  else {
+    /* we are probably not empty */
+    if (rank == 0) {
+      first_tree = p4est->first_local_tree = 0;
+      p4est_quadrant_set_morton (&f, maxlevel, 0);
+    }
+    else {
+      first_tree = p4est->first_local_tree = points->p.which_tree;
+      p4est_node_to_quadrant (points, maxlevel, &f);
+    }
+    first_quad = &f;
+  }
+  last_tree = p4est->last_local_tree = -2;
+  p4est_comm_global_partition (p4est, first_quad);
+  first_quad = p4est->global_first_position + rank;
+  next_quad = p4est->global_first_position + (rank + 1);
+  next_tree = next_quad->p.which_tree;
+  if (first_tree >= 0 &&
+      p4est_quadrant_is_equal (first_quad, next_quad) &&
+      first_quad->p.which_tree == next_quad->p.which_tree) {
+    /* if all our points are consumed by the next processor we are empty */
+    first_tree = p4est->first_local_tree = -1;
+  }
+  if (first_tree >= 0) {
+    /* we are definitely not empty */
+    if (next_quad->x == 0 && next_quad->y == 0
+#ifdef P4_TO_P8
+        && next_quad->z == 0
+#endif
+      ) {
+      last_tree = p4est->last_local_tree = next_tree - 1;
+    }
+    else {
+      last_tree = p4est->last_local_tree = next_tree;
+    }
+    P4EST_ASSERT (first_tree <= last_tree);
+  }
+
+  /* fill the local trees */
+  P4EST_QUADRANT_INIT (&a);
+  P4EST_QUADRANT_INIT (&b);
+  P4EST_QUADRANT_INIT (&c);
+  P4EST_QUADRANT_INIT (&l);
+  n = *next_quad;
+  n.level = (int8_t) maxlevel;
+  for (jt = first_tree; jt <= last_tree; ++jt) {
+    int                 onlyone = 0;
+    int                 includeb = 0;
+
+    tree = p4est_tree_array_index (p4est->trees, jt);
+
+    /* determine first local quadrant of this tree */
+    if (jt == first_tree) {
+      a = *first_quad;
+      a.level = (int8_t) maxlevel;
+      first_quad = next_quad = NULL;    /* free to use further down */
+      P4EST_ASSERT (p4est_quadrant_is_valid (&a));
+    }
+    else {
+      p4est_quadrant_set_morton (&a, maxlevel, 0);
+      P4EST_ASSERT (jt < next_tree || p4est_quadrant_compare (&a, &n) < 0);
+    }
+
+    /* enlarge first local quadrant if possible */
+    if (jt < next_tree) {
+      while (p4est_quadrant_child_id (&a) == 0 && a.level > 0) {
+        p4est_quadrant_parent (&a, &a);
+      }
+      P4EST_ASSERT (jt == first_tree || a.level == 0);
+    }
+    else {
+      for (c = a; p4est_quadrant_child_id (&c) == 0; a = c) {
+        p4est_quadrant_parent (&c, &c);
+        p4est_quadrant_last_descendant (&c, &l, maxlevel);
+        if (p4est_quadrant_compare (&l, &n) >= 0) {
+          break;
+        }
+      }
+      P4EST_ASSERT (a.level > 0);
+      P4EST_ASSERT ((p4est_quadrant_last_descendant (&a, &l, maxlevel),
+                     p4est_quadrant_compare (&l, &n) < 0));
+    }
+    p4est_quadrant_first_descendant (&a, &tree->first_desc, P4EST_QMAXLEVEL);
+
+    /* determine largest possible last quadrant of this tree */
+    if (jt < next_tree) {
+      p4est_quadrant_last_descendant (&a, &l, maxlevel);
+      p4est_quadrant_set_morton (&b, 0, 0);
+      p4est_quadrant_last_descendant (&b, &b, maxlevel);
+      if (p4est_quadrant_is_equal (&l, &b)) {
+        onlyone = 1;
+      }
+      else {
+        includeb = 1;
+        for (c = b; p4est_quadrant_child_id (&c) == P4EST_CHILDREN - 1; b = c) {
+          p4est_quadrant_parent (&c, &c);
+          p4est_quadrant_first_descendant (&c, &f, maxlevel);
+          if (p4est_quadrant_compare (&l, &f) >= 0) {
+            break;
+          }
+        }
+      }
+    }
+    else {
+      b = n;
+    }
+
+    /* create a complete tree */
+    if (onlyone) {
+      quad = p4est_quadrant_array_push (&tree->quadrants);
+      *quad = a;
+      p4est_quadrant_init_data (p4est, jt, quad, p4est_points_init);
+      tree->maxlevel = a.level;
+      ++tree->quadrants_per_level[a.level];
+    }
+    else {
+      p4est_complete_region (p4est, &a, 1, &b, includeb,
+                             tree, jt, p4est_points_init);
+      quad = p4est_quadrant_array_index (&tree->quadrants,
+                                         tree->quadrants.elem_count - 1);
+    }
+    tree->quadrants_offset = p4est->local_num_quadrants;
+    p4est->local_num_quadrants += tree->quadrants.elem_count;
+    p4est_quadrant_last_descendant (quad, &tree->last_desc, P4EST_QMAXLEVEL);
+
+    /* verification */
+#ifdef P4EST_ENABLE_DEBUG
+    first_quad = p4est_quadrant_array_index (&tree->quadrants, 0);
+    for (zz = 1; zz < tree->quadrants.elem_count; ++zz) {
+      next_quad = p4est_quadrant_array_index (&tree->quadrants, zz);
+      P4EST_ASSERT (((p4est_locidx_t *) first_quad->p.user_data)[1] ==
+                    ((p4est_locidx_t *) next_quad->p.user_data)[0]);
+      first_quad = next_quad;
+    }
+#endif
+  }
+  if (last_tree >= 0) {
+    for (; jt < num_trees; ++jt) {
+      tree = p4est_tree_array_index (p4est->trees, jt);
+      tree->quadrants_offset = p4est->local_num_quadrants;
+    }
+  }
+
+  /* compute some member variables */
+  p4est->global_first_quadrant = P4EST_ALLOC (p4est_gloidx_t, num_procs + 1);
+  p4est_comm_count_quadrants (p4est);
+
+  /* print more statistics */
+  P4EST_VERBOSEF ("total local quadrants %lld\n",
+                  (long long) p4est->local_num_quadrants);
+
+  P4EST_ASSERT (p4est_is_valid (p4est));
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done " P4EST_STRING
+                            "_new_points with %lld total quadrants\n",
+                            (long long) p4est->global_num_quadrants);
+
+  /* refine to have one point per quadrant */
+  if (max_points >= 0) {
+    p4est_refine_ext (p4est, 1, maxlevel, p4est_points_refine,
+                      p4est_points_init, NULL);
+#ifdef P4EST_ENABLE_DEBUG
+    for (jt = first_tree; jt <= last_tree; ++jt) {
+      tree = p4est_tree_array_index (p4est->trees, jt);
+      first_quad = p4est_quadrant_array_index (&tree->quadrants, 0);
+      for (zz = 1; zz < tree->quadrants.elem_count; ++zz) {
+        next_quad = p4est_quadrant_array_index (&tree->quadrants, zz);
+        P4EST_ASSERT (((p4est_locidx_t *) first_quad->p.user_data)[1] ==
+                      ((p4est_locidx_t *) next_quad->p.user_data)[0]);
+        first_quad = next_quad;
+      }
+    }
+#endif
+  }
+
+  /* initialize user pointer and data size */
+  p4est_reset_data (p4est, data_size, init_fn, user_pointer);
+
+  return p4est;
+}
diff --git a/src/p4est_points.h b/src/p4est_points.h
new file mode 100644
index 0000000..169bbeb
--- /dev/null
+++ b/src/p4est_points.h
@@ -0,0 +1,74 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/********************************************************************
+ *                          IMPORTANT NOTE                          *
+ *                                                                  *
+ * The p4est_points functionality depends on sc/src/sc_sort.        *
+ * That parallel bitonic sort is still buggy (see sc/bugs).         *
+ * If you want to use this code you have to fix the sort first.     *
+ ********************************************************************/
+
+#ifndef P4EST_POINTS_H
+#define P4EST_POINTS_H
+
+#include <p4est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Create a new forest based on a distributed set of points.
+ *
+ * \param [in] mpicomm       A valid MPI communicator.
+ * \param [in] connectivity  This is the connectivity information that
+ *                           the forest is built with.  Note the p4est
+ *                           does not take ownership of the memory.
+ * \param [in] maxlevel      Level of the smallest possible quadrants.
+ * \param [in] points        Unsorted collection of clamped quadrant nodes.
+ *                           The tree id must be stored in p.which_tree.
+ * \param [in] num_points    Number of local points provided in the array.
+ * \param [in] max_points    Maximum number of points per quadrant.
+ *                           Applies to quadrants above maxlevel, so 0 is ok.
+ *                           A value of -1 disables all refinement.
+ * \param [in] data_size     This is the size of data for each quadrant which
+ *                           can be zero.  Then user_data_pool is set to NULL.
+ * \param [in] init_fn       Callback function to initialize the user_data
+ *                           which is already allocated automatically.
+ * \param [in] user_pointer  Assign to the user_pointer member of the p4est
+ *                           before init_fn is called the first time.
+ *
+ * \return This returns a valid forest.
+ *
+ * \note The connectivity structure must not be destroyed
+ *       during the lifetime of this forest.
+ */
+p4est_t            *p4est_new_points (sc_MPI_Comm mpicomm,
+                                      p4est_connectivity_t * connectivity,
+                                      int maxlevel, p4est_quadrant_t * points,
+                                      p4est_locidx_t num_points,
+                                      p4est_locidx_t max_points,
+                                      size_t data_size, p4est_init_t init_fn,
+                                      void *user_pointer);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_POINTS_H */
diff --git a/src/p4est_search.c b/src/p4est_search.c
new file mode 100644
index 0000000..63e6ef6
--- /dev/null
+++ b/src/p4est_search.c
@@ -0,0 +1,613 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_search.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_search.h>
+#endif
+
+ssize_t
+p4est_find_lower_bound (sc_array_t * array,
+                        const p4est_quadrant_t * q, size_t guess)
+{
+  int                 comp;
+  size_t              count;
+  size_t              quad_low, quad_high;
+  p4est_quadrant_t   *cur;
+
+  count = array->elem_count;
+  if (count == 0)
+    return -1;
+
+  quad_low = 0;
+  quad_high = count - 1;
+
+  for (;;) {
+    P4EST_ASSERT (quad_low <= quad_high);
+    P4EST_ASSERT (quad_low < count && quad_high < count);
+    P4EST_ASSERT (quad_low <= guess && guess <= quad_high);
+
+    /* compare two quadrants */
+    cur = p4est_quadrant_array_index (array, guess);
+    comp = p4est_quadrant_compare (q, cur);
+
+    /* check if guess is higher or equal q and there's room below it */
+    if (comp <= 0 && (guess > 0 && p4est_quadrant_compare (q, cur - 1) <= 0)) {
+      quad_high = guess - 1;
+      guess = (quad_low + quad_high + 1) / 2;
+      continue;
+    }
+
+    /* check if guess is lower than q */
+    if (comp > 0) {
+      quad_low = guess + 1;
+      if (quad_low > quad_high)
+        return -1;
+
+      guess = (quad_low + quad_high) / 2;
+      continue;
+    }
+
+    /* otherwise guess is the correct quadrant */
+    break;
+  }
+
+  return (ssize_t) guess;
+}
+
+ssize_t
+p4est_find_higher_bound (sc_array_t * array,
+                         const p4est_quadrant_t * q, size_t guess)
+{
+  int                 comp;
+  size_t              count;
+  size_t              quad_low, quad_high;
+  p4est_quadrant_t   *cur;
+
+  count = array->elem_count;
+  if (count == 0)
+    return -1;
+
+  quad_low = 0;
+  quad_high = count - 1;
+
+  for (;;) {
+    P4EST_ASSERT (quad_low <= quad_high);
+    P4EST_ASSERT (quad_low < count && quad_high < count);
+    P4EST_ASSERT (quad_low <= guess && guess <= quad_high);
+
+    /* compare two quadrants */
+    cur = p4est_quadrant_array_index (array, guess);
+    comp = p4est_quadrant_compare (cur, q);
+
+    /* check if guess is lower or equal q and there's room above it */
+    if (comp <= 0 &&
+        (guess < count - 1 && p4est_quadrant_compare (cur + 1, q) <= 0)) {
+      quad_low = guess + 1;
+      guess = (quad_low + quad_high) / 2;
+      continue;
+    }
+
+    /* check if guess is higher than q */
+    if (comp > 0) {
+      if (guess == 0)
+        return -1;
+
+      quad_high = guess - 1;
+      if (quad_high < quad_low)
+        return -1;
+
+      guess = (quad_low + quad_high + 1) / 2;
+      continue;
+    }
+
+    /* otherwise guess is the correct quadrant */
+    break;
+  }
+
+  return (ssize_t) guess;
+}
+
+static              size_t
+p4est_array_split_ancestor_id (sc_array_t * array, size_t zindex, void *data)
+{
+  int                *levelp = (int *) data;
+  p4est_quadrant_t   *q = p4est_quadrant_array_index (array, zindex);
+
+  return ((size_t) p4est_quadrant_ancestor_id (q, *levelp));
+}
+
+void
+p4est_split_array (sc_array_t * array, int level, size_t indices[])
+{
+  size_t              count = array->elem_count;
+  sc_array_t          view;
+#ifdef P4EST_ENABLE_DEBUG
+  p4est_quadrant_t   *test1, test2;
+  p4est_quadrant_t   *cur;
+#endif
+
+  P4EST_ASSERT (0 <= level && level < P4EST_QMAXLEVEL);
+  /** If empty, return all zeroes */
+  if (count == 0) {
+    indices[0] = indices[1] = indices[2] = indices[3] = indices[4] =
+#ifdef P4_TO_P8
+      indices[5] = indices[6] = indices[7] = indices[8] =
+#endif
+      0;
+    return;
+  }
+
+  P4EST_ASSERT (sc_array_is_sorted (array, p4est_quadrant_compare));
+#ifdef P4EST_ENABLE_DEBUG
+  cur = p4est_quadrant_array_index (array, 0);
+  P4EST_ASSERT ((int) cur->level > level);
+  test1 = p4est_quadrant_array_index (array, count - 1);
+  P4EST_ASSERT ((int) test1->level > level);
+  p4est_nearest_common_ancestor (cur, test1, &test2);
+  P4EST_ASSERT ((int) test2.level >= level);
+#endif
+
+  sc_array_init_data (&view, indices, sizeof (size_t), P4EST_CHILDREN + 1);
+  level++;
+  sc_array_split (array, &view, P4EST_CHILDREN, p4est_array_split_ancestor_id,
+                  &level);
+}
+
+/** If we suppose a range of quadrants touches a corner of a tree, then it must
+ * also touch the faces (and edges) that touch that corner.
+ */
+#ifndef P4_TO_P8
+/* *INDENT-OFF* */
+static int32_t p4est_corner_boundaries[4] =
+{             /*                           |corners | faces */
+  0x00000015, /* 0000 0000 0000 0000 0000 0000| 0001| 0101  */
+  0x00000026, /* 0000 0000 0000 0000 0000 0000| 0010| 0110  */
+  0x00000049, /* 0000 0000 0000 0000 0000 0000| 0100| 1001  */
+  0x0000008a  /* 0000 0000 0000 0000 0000 0000| 1000| 1010  */
+};
+/* *INDENT-ON* */
+static int32_t      p4est_all_boundaries = 0x000000ff;
+#else
+/* *INDENT-OFF* */
+static int32_t p4est_corner_boundaries[8] =
+{             /*        |corners   |edges          |faces   */
+  0x00044455, /* 0000 00|00 0000 01|00 0100 0100 01|01 0101 */
+  0x00088856, /* 0000 00|00 0000 10|00 1000 1000 01|01 0110 */
+  0x00110499, /* 0000 00|00 0001 00|01 0000 0100 10|01 1001 */
+  0x0022089a, /* 0000 00|00 0010 00|10 0000 1000 10|01 1010 */
+  0x00405125, /* 0000 00|00 0100 00|00 0101 0001 00|10 0101 */
+  0x0080a126, /* 0000 00|00 1000 00|00 1010 0001 00|10 0110 */
+  0x01011229, /* 0000 00|01 0000 00|01 0001 0010 00|10 1001 */
+  0x0202222a  /* 0000 00|10 0000 00|10 0010 0010 00|10 1010 */
+};
+/* *INDENT-ON* */
+static int32_t      p4est_all_boundaries = 0x03ffffff;
+#endif
+
+static              int32_t
+p4est_limit_boundaries (p4est_quadrant_t * q, int dir, int limit,
+                        int last_level, int level, int32_t touch,
+                        int32_t mask)
+{
+  int                 cid;
+  int32_t             next;
+
+  P4EST_ASSERT (q->level == P4EST_QMAXLEVEL);
+  P4EST_ASSERT (level <= P4EST_QMAXLEVEL);
+  P4EST_ASSERT (level <= last_level);
+  if ((mask & ~touch) == 0) {
+    return touch;
+  }
+  cid = p4est_quadrant_ancestor_id (q, level);
+  next = p4est_corner_boundaries[cid] & mask;
+  cid += dir;
+  while (cid != limit) {
+    touch |= (p4est_corner_boundaries[cid] & mask);
+    cid += dir;
+  }
+  if (level == last_level) {
+    return (touch | next);
+  }
+  return p4est_limit_boundaries (q, dir, limit, last_level, level + 1, touch,
+                                 next);
+}
+
+static              int32_t
+p4est_range_boundaries (p4est_quadrant_t * lq, p4est_quadrant_t * uq,
+                        int alevel, int level, int32_t mask)
+{
+  int                 i, lcid, ucid, cid;
+  int32_t             lnext, unext, touch;
+  p4est_qcoord_t      x, y, a;
+#ifdef P4_TO_P8
+  p4est_qcoord_t      z;
+#endif
+  const p4est_qcoord_t shift = P4EST_QUADRANT_LEN (P4EST_QMAXLEVEL);
+  int                 count;
+  int                 last_level;
+
+  P4EST_ASSERT (level <= alevel + 1);
+
+  if (mask == 0) {
+    return 0;
+  }
+  if (level == alevel + 1) {
+    lcid = p4est_quadrant_ancestor_id (lq, level);
+    ucid = p4est_quadrant_ancestor_id (uq, level);
+    P4EST_ASSERT (lcid < ucid);
+    lnext = (p4est_corner_boundaries[lcid] & mask);
+    unext = (p4est_corner_boundaries[ucid] & mask);
+    touch = 0;
+    for (i = lcid + 1; i < ucid; i++) {
+      touch |= (p4est_corner_boundaries[i] & mask);
+    }
+
+    cid = p4est_quadrant_child_id (lq);
+    x = lq->x + ((cid & 1) ? shift : 0);
+    y = lq->y + (((cid >> 1) & 1) ? shift : 0);
+#ifdef P4_TO_P8
+    z = lq->z + ((cid >> 2) ? shift : 0);
+#endif
+    a = ~(x | y
+#ifdef P4_TO_P8
+          | z
+#endif
+      );
+    count = 0;
+    while ((a & ((p4est_qcoord_t) 1)) && count <= P4EST_MAXLEVEL) {
+      a >>= 1;
+      count++;
+    }
+    last_level = (P4EST_MAXLEVEL - count) + 1;
+    if (last_level <= level) {
+      touch |= lnext;
+    }
+    else {
+      P4EST_ASSERT (last_level <= P4EST_QMAXLEVEL);
+      touch |= p4est_limit_boundaries (lq, 1, P4EST_CHILDREN, last_level,
+                                       level + 1, touch, lnext);
+    }
+
+    cid = p4est_quadrant_child_id (uq);
+    x = uq->x + ((cid & 1) ? shift : 0);
+    y = uq->y + (((cid >> 1) & 1) ? shift : 0);
+#ifdef P4_TO_P8
+    z = uq->z + ((cid >> 2) ? shift : 0);
+#endif
+    a = ~(x | y
+#ifdef P4_TO_P8
+          | z
+#endif
+      );
+    count = 0;
+    while ((a & ((p4est_qcoord_t) 1)) && count <= P4EST_MAXLEVEL) {
+      a >>= 1;
+      count++;
+    }
+    last_level = (P4EST_MAXLEVEL - count) + 1;
+    if (last_level <= level) {
+      touch |= unext;
+    }
+    else {
+      P4EST_ASSERT (last_level <= P4EST_QMAXLEVEL);
+      touch |= p4est_limit_boundaries (uq, -1, -1, last_level, level + 1,
+                                       touch, unext);
+    }
+
+    return touch;
+  }
+  lcid = p4est_quadrant_ancestor_id (lq, level);
+  P4EST_ASSERT (p4est_quadrant_ancestor_id (uq, level) == lcid);
+  return p4est_range_boundaries (lq, uq, alevel, level + 1,
+                                 (p4est_corner_boundaries[lcid] & mask));
+}
+
+int32_t
+p4est_find_range_boundaries (p4est_quadrant_t * lq, p4est_quadrant_t * uq,
+                             int level, int faces[],
+#ifdef P4_TO_P8
+                             int edges[],
+#endif
+                             int corners[])
+{
+  int                 i;
+  p4est_quadrant_t    a;
+  int                 alevel;
+  int32_t             touch;
+  int32_t             mask = 0x00000001;
+  p4est_qcoord_t      x, y, all;
+#ifdef P4_TO_P8
+  p4est_qcoord_t      z;
+#endif
+  const p4est_qcoord_t shift = P4EST_QUADRANT_LEN (P4EST_QMAXLEVEL);
+  int                 count;
+  int                 last_level;
+  int                 cid;
+
+  P4EST_ASSERT (level >= 0 && level <= P4EST_QMAXLEVEL);
+  if ((lq == NULL && uq == NULL) || level == P4EST_QMAXLEVEL) {
+    touch = p4est_all_boundaries;
+    goto find_range_boundaries_exit;
+  }
+
+  if (lq == NULL) {
+    P4EST_ASSERT (uq->level == P4EST_QMAXLEVEL);
+
+    cid = p4est_quadrant_child_id (uq);
+    x = uq->x + ((cid & 1) ? shift : 0);
+    y = uq->y + (((cid >> 1) & 1) ? shift : 0);
+#ifdef P4_TO_P8
+    z = uq->z + ((cid >> 2) ? shift : 0);
+#endif
+    all = ~(x | y
+#ifdef P4_TO_P8
+            | z
+#endif
+      );
+    count = 0;
+    while ((all & ((p4est_qcoord_t) 1)) && count <= P4EST_MAXLEVEL) {
+      all >>= 1;
+      count++;
+    }
+    last_level = (P4EST_MAXLEVEL - count) + 1;
+    last_level = (last_level <= level) ? level + 1 : last_level;
+
+    P4EST_ASSERT (last_level <= P4EST_QMAXLEVEL);
+
+    touch = p4est_limit_boundaries (uq, -1, -1, last_level, level + 1, 0,
+                                    p4est_all_boundaries);
+  }
+  else if (uq == NULL) {
+    P4EST_ASSERT (lq->level == P4EST_QMAXLEVEL);
+
+    cid = p4est_quadrant_child_id (lq);
+    x = lq->x + ((cid & 1) ? shift : 0);
+    y = lq->y + (((cid >> 1) & 1) ? shift : 0);
+#ifdef P4_TO_P8
+    z = lq->z + ((cid >> 2) ? shift : 0);
+#endif
+    all = ~(x | y
+#ifdef P4_TO_P8
+            | z
+#endif
+      );
+    count = 0;
+    while ((all & ((p4est_qcoord_t) 1)) && count <= P4EST_MAXLEVEL) {
+      all >>= 1;
+      count++;
+    }
+    last_level = (P4EST_MAXLEVEL - count) + 1;
+    last_level = (last_level <= level) ? level + 1 : last_level;
+
+    P4EST_ASSERT (last_level <= P4EST_QMAXLEVEL);
+
+    touch = p4est_limit_boundaries (lq, 1, P4EST_CHILDREN, last_level,
+                                    level + 1, 0, p4est_all_boundaries);
+  }
+  else {
+    P4EST_ASSERT (uq->level == P4EST_QMAXLEVEL);
+    P4EST_ASSERT (lq->level == P4EST_QMAXLEVEL);
+    p4est_nearest_common_ancestor (lq, uq, &a);
+    alevel = (int) a.level;
+    P4EST_ASSERT (alevel >= level);
+    touch = p4est_range_boundaries (lq, uq, alevel, level + 1,
+                                    p4est_all_boundaries);
+  }
+
+find_range_boundaries_exit:
+  if (faces != NULL) {
+    for (i = 0; i < P4EST_FACES; i++) {
+      faces[i] = (touch & mask) != 0;
+      mask <<= 1;
+    }
+  }
+  else {
+    mask <<= P4EST_FACES;
+  }
+#ifdef P4_TO_P8
+  if (edges != NULL) {
+    for (i = 0; i < P8EST_EDGES; i++) {
+      edges[i] = (touch & mask) != 0;
+      mask <<= 1;
+    }
+  }
+  else {
+    mask <<= P8EST_EDGES;
+  }
+#endif
+  if (corners != NULL) {
+    for (i = 0; i < P4EST_CHILDREN; i++) {
+      corners[i] = (touch & mask) != 0;
+      mask <<= 1;
+    }
+  }
+
+  return touch;
+}
+
+static void
+p4est_search_recursion (p4est_t * p4est, p4est_topidx_t which_tree,
+                        p4est_quadrant_t * quadrant,
+                        p4est_search_query_t search_quadrant_fn,
+                        p4est_search_query_t search_point_fn,
+                        sc_array_t * quadrants,
+                        sc_array_t * points, sc_array_t * actives)
+{
+  int                 i;
+  int                 is_leaf, is_match;
+  size_t              qcount = quadrants->elem_count;
+  size_t              zz, *pz, *qz;
+  size_t              split[P4EST_CHILDREN + 1];
+  p4est_locidx_t      local_num;
+  p4est_quadrant_t   *q, *lq, children[P4EST_CHILDREN];
+  sc_array_t          child_quadrants, child_actives;
+
+  /*
+   * Invariants of the recursion:
+   * 1. quadrant is larger or equal in size than those in the array.
+   * 2. quadrant is equal to or an ancestor of those in the array.
+   */
+
+  P4EST_ASSERT (actives->elem_count <= points->elem_count);
+
+  /* return if there are no quadrants or active points */
+  if (qcount == 0 || actives->elem_count == 0)
+    return;
+
+  /* determine leaf situation */
+  q = p4est_quadrant_array_index (quadrants, 0);
+  if (qcount > 1) {
+    P4EST_ASSERT (p4est_quadrant_is_ancestor (quadrant, q));
+    is_leaf = 0;
+    local_num = -1;
+    lq = p4est_quadrant_array_index (quadrants, quadrants->elem_count - 1);
+    if (p4est_quadrant_ancestor_id (q, quadrant->level + 1) ==
+        p4est_quadrant_ancestor_id (lq, quadrant->level + 1)) {
+      p4est_nearest_common_ancestor (q, lq, quadrant);
+      P4EST_ASSERT (p4est_quadrant_is_ancestor (quadrant, q));
+      P4EST_ASSERT (p4est_quadrant_is_ancestor (quadrant, lq));
+    }
+  }
+  else {
+    p4est_locidx_t      offset;
+    p4est_tree_t       *tree;
+
+    P4EST_ASSERT (p4est_quadrant_is_equal (quadrant, q) ||
+                  p4est_quadrant_is_ancestor (quadrant, q));
+    is_leaf = 1;
+
+    /* determine offset of quadrant in local forest */
+    tree = p4est_tree_array_index (p4est->trees, which_tree);
+    offset = (p4est_locidx_t) ((quadrants->array - tree->quadrants.array)
+                               / sizeof (p4est_quadrant_t));
+    P4EST_ASSERT (offset >= 0 &&
+                  (size_t) offset < tree->quadrants.elem_count);
+    local_num = tree->quadrants_offset + offset;
+    quadrant = q;
+  }
+
+  /* execute quadrant callback if present, which may stop the recursion */
+  if (search_quadrant_fn != NULL &&
+      !search_quadrant_fn (p4est, which_tree, quadrant, local_num, NULL)) {
+    return;
+  }
+
+  /* query callback for all points and return if none remain */
+  sc_array_init (&child_actives, sizeof (size_t));
+  for (zz = 0; zz < actives->elem_count; ++zz) {
+    pz = (size_t *) sc_array_index (actives, zz);
+    is_match = search_point_fn (p4est, which_tree, quadrant, local_num,
+                                sc_array_index (points, *pz));
+    if (!is_leaf && is_match) {
+      qz = (size_t *) sc_array_push (&child_actives);
+      *qz = *pz;
+    }
+  }
+  if (child_actives.elem_count == 0)
+    return;
+
+  /* leaf situation has returned above */
+  P4EST_ASSERT (!is_leaf);
+
+  /* split quadrant array and run recursion */
+  p4est_split_array (quadrants, (int) quadrant->level, split);
+  p4est_quadrant_childrenv (quadrant, children);
+  for (i = 0; i < P4EST_CHILDREN; ++i) {
+    if (split[i] < split[i + 1]) {
+      sc_array_init_view (&child_quadrants, quadrants,
+                          split[i], split[i + 1] - split[i]);
+      p4est_search_recursion (p4est, which_tree, &children[i],
+                              search_quadrant_fn, search_point_fn,
+                              &child_quadrants, points, &child_actives);
+      sc_array_reset (&child_quadrants);
+    }
+  }
+  sc_array_reset (&child_actives);
+}
+
+void
+p4est_search (p4est_t * p4est, p4est_search_query_t search_quadrant_fn,
+              p4est_search_query_t search_point_fn, sc_array_t * points)
+{
+  p4est_topidx_t      jt;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t    root;
+  p4est_quadrant_t    temp, temp2;
+  p4est_quadrant_t   *f, *l;
+  uint64_t            midx;
+  sc_array_t          actives;
+  sc_array_t         *tquadrants;
+  size_t              zz, *pz;
+
+  for (jt = p4est->first_local_tree; jt <= p4est->last_local_tree; ++jt) {
+
+    /* start recursion with root quadrant */
+    p4est_quadrant_set_morton (&root, 0, 0);
+    f = NULL;
+    l = NULL;
+    if (jt == p4est->first_local_tree) {
+      f = &p4est->global_first_position[p4est->mpirank];
+      p4est_quadrant_last_descendant (&root, &temp, P4EST_QMAXLEVEL);
+      l = &temp;
+    }
+    if (jt == p4est->last_local_tree) {
+      if (!f) {
+        p4est_quadrant_first_descendant (&root, &temp, P4EST_QMAXLEVEL);
+        f = &temp;
+      }
+      l = &p4est->global_first_position[p4est->mpirank + 1];
+      if (l->p.which_tree == jt) {
+        midx = p4est_quadrant_linear_id (l, P4EST_QMAXLEVEL);
+        p4est_quadrant_set_morton (&temp2, P4EST_QMAXLEVEL, midx - 1);
+        l = &temp2;
+      }
+      else {
+        p4est_quadrant_last_descendant (&root, &temp2, P4EST_QMAXLEVEL);
+        l = &temp2;
+      }
+    }
+    if (f != NULL) {
+      P4EST_ASSERT (l != NULL);
+      p4est_nearest_common_ancestor (f, l, &root);
+    }
+
+    /* grab complete tree quadrant array */
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    tquadrants = &tree->quadrants;
+
+    /* mark all points as active */
+    sc_array_init_size (&actives, sizeof (size_t), points->elem_count);
+    for (zz = 0; zz < points->elem_count; ++zz) {
+      pz = (size_t *) sc_array_index (&actives, zz);
+      *pz = zz;
+    }
+
+    p4est_search_recursion (p4est, jt, &root, search_quadrant_fn,
+                            search_point_fn, tquadrants, points, &actives);
+    sc_array_reset (&actives);
+  }
+}
diff --git a/src/p4est_search.h b/src/p4est_search.h
new file mode 100644
index 0000000..44a454f
--- /dev/null
+++ b/src/p4est_search.h
@@ -0,0 +1,156 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_SEARCH_H
+#define P4EST_SEARCH_H
+
+#include <p4est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Find the lowest position tq in a quadrant array such that tq >= q.
+ * \return  Returns the id of the matching quadrant
+ *                  or -1 if array < q or the array is empty.
+ */
+ssize_t             p4est_find_lower_bound (sc_array_t * array,
+                                            const p4est_quadrant_t * q,
+                                            size_t guess);
+
+/** Find the highest position tq in a quadrant array such that tq <= q.
+ * \return  Returns the id of the matching quadrant
+ *                  or -1 if array > q or the array is empty.
+ */
+ssize_t             p4est_find_higher_bound (sc_array_t * array,
+                                             const p4est_quadrant_t * q,
+                                             size_t guess);
+
+/** Given a sorted \a array of quadrants that have a common ancestor at level
+ * \a level, compute the \a indices of the first quadrant in each of the common
+ * ancestor's children at level \a level + 1;
+ * \param [in] array     The sorted array of quadrants of level > \a level.
+ * \param [in] level     The level at which there is a common ancestor.
+ * \param [in,out] indices     The indices of the first quadrant in each of
+ *                             the ancestors's children, plus an additional
+ *                             index on the end.  The quadrants of \a array
+ *                             that are descendants of child i have indices
+ *                             between indices[i] and indices[i + 1] - 1.  If
+ *                             indices[i] = indices[i+1], this indicates that
+ *                             no quadrant in the array is contained in
+ *                             child i.
+ */
+void                p4est_split_array (sc_array_t * array, int level,
+                                       size_t indices[]);
+
+/** Given two smallest quadrants, \a lq and \a uq, that mark the first and the
+ * last quadrant in a range of quadrants, determine which portions of the tree
+ * boundary the range touches.
+ * \param [in] lq        The smallest quadrant at the start of the range: if
+ *                       NULL, the tree's first quadrant is taken to be the
+ *                       start of the range.
+ * \param [in] uq        The smallest quadrant at the end of the range: if
+ *                       NULL, the tree's last quadrant is taken to be the
+ *                       end of the range.
+ * \param [in] level     The level of the containing quadrant whose boundaries
+ *                       are tested: 0 if we want to test the boundaries of the
+ *                       whole tree.
+ * \param [in/out] faces       An array of size 4 that is filled: faces[i] is
+ *                             true if the range touches that face.
+ * \param [in/out] corners     An array of size 4 that is filled: corners[i] is
+ *                             true if the range touches that corner.
+ *                             \faces or \corners may be NULL.
+ * \return  Returns an int32_t encoded with the same information in \faces and
+ *          \corners: the first (least) four bits represent the four faces,
+ *          the next four bits represent the four corners.
+ */
+int32_t             p4est_find_range_boundaries (p4est_quadrant_t * lq,
+                                                 p4est_quadrant_t * uq,
+                                                 int level, int faces[],
+                                                 int corners[]);
+
+/** Callback function to query the match of a "point" with a quadrant.
+ *
+ * This function can be called in two roles:  Per-quadrant, in which case the
+ * parameter \a point is NULL, or per-point, possibly many times per quadrant.
+ *
+ * \param [in] p4est        The forest to be queried.
+ * \param [in] which_tree   The tree id under consideration.
+ * \param [in] quadrant     The quadrant under consideration.
+ *                          This quadrant may be coarser than the quadrants
+ *                          that are contained in the forest (an ancestor), in
+ *                          which case it is a temporary variable and not part
+ *                          of the forest storage.  Otherwise, it is a leaf and
+ *                          points directly into the forest storage.
+ * \param [in] local_num    If the quadrant is not a leaf, this is -1.  Otherwise
+ *                          it is the (non-negative) index of the quadrant
+ *                          relative to the processor-local quadrant storage.
+ * \param [in] point        Representation of a "point"; user-defined.
+ *                          If \a point is NULL, the callback may be used to
+ *                          prepare quadrant-related search meta data.
+ * \return                  If \a point is NULL, true if the search confined to
+ *                          \a quadrant should be executed, false to skip it.
+ *                          Else, true if point may be contained in the
+ *                          quadrant and false otherwise; the return value has
+ *                          no effect on a leaf.
+ */
+typedef int         (*p4est_search_query_t) (p4est_t * p4est,
+                                             p4est_topidx_t which_tree,
+                                             p4est_quadrant_t * quadrant,
+                                             p4est_locidx_t local_num,
+                                             void *point);
+
+/** Search "points" from a given set in the forest.
+ *
+ * The search runs over all local quadrants and proceeds recursively top-down.
+ * Its outer loop is thus a depth-first, processor-local forest traversal.
+ * Each quadrant in that loop either is a leaf, or a (direct or indirect)
+ * strict ancestor of a leaf.  On entering a new quadrant, a user-provided
+ * quadrant-callback is executed.
+ * The set of points that potentially matches a given quadrant diminishes from
+ * the root down to the leaves:  For each quadrant, an inner loop over the
+ * potentially matching points executes a point-callback for each candidate
+ * that determines whether the point may be a match.  If not, it is discarded
+ * immediately, otherwise it is passed to the next finer level.
+ * The callback is allowed to return true for the same point and more than one
+ * quadrant; in this case more than one matching quadrant may be identified.
+ * The callback is also allowed to return false for all children of a quadrant
+ * that it returned true for earlier.
+ * The points can really be anything, p4est does not perform any
+ * interpretation, just passes the pointer along to the callback function.
+ *
+ * \param [in] p4est        The forest to be searched.
+ * \param [in] search_quadrant_fn   Executed once for each quadrant that is
+ *                          entered.  This quadrant is always local.  If the
+ *                          callback returns false, this quadrant and its
+ *                          descendants are excluded from the search.
+ *                          May be NULL in which case it is ignored.
+ * \param [in] search_point_fn      Must return true for a possible match.
+ * \param [in] points       User-defined array of "points".
+ */
+void                p4est_search (p4est_t * p4est,
+                                  p4est_search_query_t search_quadrant_fn,
+                                  p4est_search_query_t search_point_fn,
+                                  sc_array_t * points);
+
+SC_EXTERN_C_END;
+
+#endif
diff --git a/src/p4est_to_p8est.h b/src/p4est_to_p8est.h
new file mode 100644
index 0000000..ccde2b9
--- /dev/null
+++ b/src/p4est_to_p8est.h
@@ -0,0 +1,409 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_to_p8est.h
+ *
+ * transforms 2D \ref p4est routines into 3D \ref p8est routines
+ */
+
+#ifndef P4EST_TO_P8EST_H
+#define P4EST_TO_P8EST_H
+
+#ifdef P4EST_H
+#error "The include files p4est.h and p4est_to_p8est.h cannot be combined"
+#endif
+#define P4_TO_P8
+
+#include <p4est_base.h>
+
+/* redefine macros */
+#define P4EST_ONDISK_FORMAT             P8EST_ONDISK_FORMAT
+#define P4EST_DIM                       P8EST_DIM
+#define P4EST_FACES                     P8EST_FACES
+#define P4EST_CHILDREN                  P8EST_CHILDREN
+#define P4EST_HALF                      P8EST_HALF
+#define P4EST_FTRANSFORM                P8EST_FTRANSFORM
+#define P4EST_INSUL                     P8EST_INSUL
+#define P4EST_STRING                    P8EST_STRING
+#define P4EST_MAXLEVEL                  P8EST_MAXLEVEL
+#define P4EST_QMAXLEVEL                 P8EST_QMAXLEVEL
+#define P4EST_ROOT_LEN                  P8EST_ROOT_LEN
+#define P4EST_QUADRANT_LEN              P8EST_QUADRANT_LEN
+#define P4EST_LAST_OFFSET               P8EST_LAST_OFFSET
+#define P4EST_QUADRANT_INIT             P8EST_QUADRANT_INIT
+
+/* redefine enums */
+#define P4EST_COMM_COUNT_PERTREE        P8EST_COMM_COUNT_PERTREE
+#define P4EST_COMM_BALANCE_FIRST_COUNT  P8EST_COMM_BALANCE_FIRST_COUNT
+#define P4EST_COMM_BALANCE_FIRST_LOAD   P8EST_COMM_BALANCE_FIRST_LOAD
+#define P4EST_COMM_BALANCE_SECOND_COUNT P8EST_COMM_BALANCE_SECOND_COUNT
+#define P4EST_COMM_BALANCE_SECOND_LOAD  P8EST_COMM_BALANCE_SECOND_LOAD
+#define P4EST_COMM_PARTITION_GIVEN      P8EST_COMM_PARTITION_GIVEN
+#define P4EST_COMM_PARTITION_WEIGHTED_LOW P8EST_COMM_PARTITION_WEIGHTED_LOW
+#define P4EST_COMM_PARTITION_WEIGHTED_HIGH P8EST_COMM_PARTITION_WEIGHTED_HIGH
+#define P4EST_COMM_PARTITION_CORRECTION P8EST_COMM_PARTITION_CORRECTION
+#define P4EST_COMM_GHOST_COUNT          P8EST_COMM_GHOST_COUNT
+#define P4EST_COMM_GHOST_LOAD           P8EST_COMM_GHOST_LOAD
+#define P4EST_COMM_GHOST_EXCHANGE       P8EST_COMM_GHOST_EXCHANGE
+#define P4EST_COMM_GHOST_EXPAND_COUNT   P8EST_COMM_GHOST_EXPAND_COUNT
+#define P4EST_COMM_GHOST_EXPAND_LOAD    P8EST_COMM_GHOST_EXPAND_LOAD
+#define P4EST_COMM_GHOST_SUPPORT_COUNT  P8EST_COMM_GHOST_SUPPORT_COUNT
+#define P4EST_COMM_GHOST_SUPPORT_LOAD   P8EST_COMM_GHOST_SUPPORT_LOAD
+#define P4EST_COMM_GHOST_CHECKSUM       P8EST_COMM_GHOST_CHECKSUM
+#define P4EST_COMM_NODES_QUERY          P8EST_COMM_NODES_QUERY
+#define P4EST_COMM_NODES_REPLY          P8EST_COMM_NODES_REPLY
+#define P4EST_COMM_SAVE                 P8EST_COMM_SAVE
+#define P4EST_COMM_LNODES_TEST          P8EST_COMM_LNODES_TEST
+#define P4EST_COMM_LNODES_PASS          P8EST_COMM_LNODES_PASS
+#define P4EST_COMM_LNODES_OWNED         P8EST_COMM_LNODES_OWNED
+#define P4EST_COMM_LNODES_ALL           P8EST_COMM_LNODES_ALL
+#define P4EST_CONNECT_FACE              P8EST_CONNECT_FACE
+#define P4EST_CONNECT_CORNER            P8EST_CONNECT_CORNER
+#define P4EST_CONNECT_FULL              P8EST_CONNECT_FULL
+#define P4EST_CONN_ENCODE_NONE          P8EST_CONN_ENCODE_NONE
+#define P4EST_WRAP_NONE                 P8EST_WRAP_NONE
+#define P4EST_WRAP_REFINE               P8EST_WRAP_REFINE
+#define P4EST_WRAP_COARSEN              P8EST_WRAP_COARSEN
+
+/* redefine types */
+#ifdef P4EST_BACKWARD_DEALII
+#define p4est_balance_type_t            p8est_balance_type_t
+#endif
+#define p4est_connect_type_t            p8est_connect_type_t
+#define p4est_connectivity_encode_t     p8est_connectivity_encode_t
+#define p4est_connectivity_t            p8est_connectivity_t
+#define p4est_corner_transform_t        p8est_corner_transform_t
+#define p4est_corner_info_t             p8est_corner_info_t
+#define p4est_geometry_t                p8est_geometry_t
+#define p4est_t                         p8est_t
+#define p4est_tree_t                    p8est_tree_t
+#define p4est_quadrant_t                p8est_quadrant_t
+#define p4est_inspect_t                 p8est_inspect_t
+#define p4est_position_t                p8est_position_t
+#define p4est_init_t                    p8est_init_t
+#define p4est_refine_t                  p8est_refine_t
+#define p4est_coarsen_t                 p8est_coarsen_t
+#define p4est_weight_t                  p8est_weight_t
+#define p4est_ghost_t                   p8est_ghost_t
+#define p4est_indep_t                   p8est_indep_t
+#define p4est_nodes_t                   p8est_nodes_t
+#define p4est_lnodes_t                  p8est_lnodes_t
+#define p4est_lnodes_code_t             p8est_lnodes_code_t
+#define p4est_lnodes_rank_t             p8est_lnodes_rank_t
+#define p4est_lnodes_buffer_t           p8est_lnodes_buffer_t
+#define p4est_iter_volume_t             p8est_iter_volume_t
+#define p4est_iter_volume_info_t        p8est_iter_volume_info_t
+#define p4est_iter_face_t               p8est_iter_face_t
+#define p4est_iter_face_info_t          p8est_iter_face_info_t
+#define p4est_iter_face_side_t          p8est_iter_face_side_t
+#define p4est_iter_corner_t             p8est_iter_corner_t
+#define p4est_iter_corner_side_t        p8est_iter_corner_side_t
+#define p4est_iter_corner_info_t        p8est_iter_corner_info_t
+#define p4est_search_query_t            p8est_search_query_t
+#define p4est_mesh_t                    p8est_mesh_t
+#define p4est_mesh_face_neighbor_t      p8est_mesh_face_neighbor_t
+#define p4est_wrap_t                    p8est_wrap_t
+#define p4est_wrap_leaf_t               p8est_wrap_leaf_t
+#define p4est_wrap_flags_t              p8est_wrap_flags_t
+
+/* redefine external variables */
+#define p4est_face_corners              p8est_face_corners
+#define p4est_face_dual                 p8est_face_dual
+#define p4est_corner_faces              p8est_corner_faces
+#define p4est_corner_face_corners       p8est_corner_face_corners
+#define p4est_child_corner_faces        p8est_child_corner_faces
+#define P4EST_DATA_UNINITIALIZED        P8EST_DATA_UNINITIALIZED
+
+/* functions in p4est_connectivity */
+#define p4est_connectivity_face_neighbor_corner_orientation \
+        p8est_connectivity_face_neighbor_corner_orientation
+#define p4est_connectivity_memory_used  p8est_connectivity_memory_used
+#define p4est_connectivity_new          p8est_connectivity_new
+#define p4est_connectivity_new_brick    p8est_connectivity_new_brick
+#define p4est_connectivity_new_byname   p8est_connectivity_new_byname
+#define p4est_connectivity_new_copy     p8est_connectivity_new_copy
+#define p4est_connectivity_destroy      p8est_connectivity_destroy
+#define p4est_connectivity_set_attr     p8est_connectivity_set_attr
+#define p4est_connectivity_is_valid     p8est_connectivity_is_valid
+#define p4est_connectivity_is_equal     p8est_connectivity_is_equal
+#define p4est_connectivity_sink         p8est_connectivity_sink
+#define p4est_connectivity_deflate      p8est_connectivity_deflate
+#define p4est_connectivity_save         p8est_connectivity_save
+#define p4est_connectivity_source       p8est_connectivity_source
+#define p4est_connectivity_inflate      p8est_connectivity_inflate
+#define p4est_connectivity_load         p8est_connectivity_load
+#define p4est_connectivity_complete     p8est_connectivity_complete
+#define p4est_connectivity_reduce       p8est_connectivity_reduce
+#define p4est_expand_face_transform     p8est_expand_face_transform
+#define p4est_find_face_transform       p8est_find_face_transform
+#define p4est_find_corner_transform     p8est_find_corner_transform
+#define p4est_corner_array_index        p8est_corner_array_index
+#define p4est_connectivity_reorder      p8est_connectivity_reorder
+#define p4est_connectivity_permute      p8est_connectivity_permute
+#define p4est_connectivity_join_faces   p8est_connectivity_join_faces
+#define p4est_connectivity_is_equivalent p8est_connectivity_is_equivalent
+#define p4est_connectivity_read_inp_stream p8est_connectivity_read_inp_stream
+#define p4est_connectivity_read_inp     p8est_connectivity_read_inp
+
+/* functions in p4est */
+#define p4est_qcoord_to_vertex          p8est_qcoord_to_vertex
+#define p4est_memory_used               p8est_memory_used
+#define p4est_new                       p8est_new
+#define p4est_destroy                   p8est_destroy
+#define p4est_copy                      p8est_copy
+#define p4est_reset_data                p8est_reset_data
+#define p4est_refine                    p8est_refine
+#define p4est_coarsen                   p8est_coarsen
+#define p4est_balance                   p8est_balance
+#define p4est_partition                 p8est_partition
+#define p4est_checksum                  p8est_checksum
+#define p4est_save                      p8est_save
+#define p4est_load                      p8est_load
+#define p4est_connect_type_int          p8est_connect_type_int
+#define p4est_connect_type_string       p8est_connect_type_string
+#define p4est_tree_array_index          p8est_tree_array_index
+#define p4est_quadrant_array_index      p8est_quadrant_array_index
+#define p4est_quadrant_array_push       p8est_quadrant_array_push
+#define p4est_quadrant_mempool_alloc    p8est_quadrant_mempool_alloc
+#define p4est_quadrant_list_pop         p8est_quadrant_list_pop
+
+/* functions in p4est_extended */
+#define p4est_replace_t                 p8est_replace_t
+#define p4est_new_ext                   p8est_new_ext
+#define p4est_mesh_new_ext              p8est_mesh_new_ext
+#define p4est_refine_ext                p8est_refine_ext
+#define p4est_coarsen_ext               p8est_coarsen_ext
+#define p4est_balance_ext               p8est_balance_ext
+#define p4est_balance_subtree_ext       p8est_balance_subtree_ext
+#define p4est_partition_ext             p8est_partition_ext
+#define p4est_save_ext                  p8est_save_ext
+#define p4est_load_ext                  p8est_load_ext
+#define p4est_source_ext                p8est_source_ext
+
+/* functions in p4est_iterate */
+#define p4est_iterate                   p8est_iterate
+#define p4est_iterate_ext               p8est_iterate_ext
+#define p4est_iter_fside_array_index    p8est_iter_fside_array_index
+#define p4est_iter_fside_array_index_int p8est_iter_fside_array_index_int
+#define p4est_iter_cside_array_index    p8est_iter_cside_array_index
+#define p4est_iter_cside_array_index_int p8est_iter_cside_array_index_int
+
+/* functions in p4est_points */
+#define p4est_new_points                p8est_new_points
+
+/* functions in p4est_bits */
+#define p4est_quadrant_print            p8est_quadrant_print
+#define p4est_quadrant_is_equal         p8est_quadrant_is_equal
+#define p4est_quadrant_overlaps         p8est_quadrant_overlaps
+#define p4est_quadrant_is_equal_piggy   p8est_quadrant_is_equal_piggy
+#define p4est_quadrant_compare          p8est_quadrant_compare
+#define p4est_quadrant_disjoint         p8est_quadrant_disjoint
+#define p4est_quadrant_compare_piggy    p8est_quadrant_compare_piggy
+#define p4est_quadrant_compare_local_num p8est_quadrant_compare_local_num
+#define p4est_quadrant_equal_fn         p8est_quadrant_equal_fn
+#define p4est_quadrant_hash_fn          p8est_quadrant_hash_fn
+#define p4est_node_equal_piggy_fn       p8est_node_equal_piggy_fn
+#define p4est_node_hash_piggy_fn        p8est_node_hash_piggy_fn
+#define p4est_node_clamp_inside         p8est_node_clamp_inside
+#define p4est_node_unclamp              p8est_node_unclamp
+#define p4est_node_to_quadrant          p8est_node_to_quadrant
+#define p4est_quadrant_contains_node    p8est_quadrant_contains_node
+#define p4est_quadrant_ancestor_id      p8est_quadrant_ancestor_id
+#define p4est_quadrant_child_id         p8est_quadrant_child_id
+#define p4est_quadrant_is_inside_root   p8est_quadrant_is_inside_root
+#define p4est_quadrant_is_inside_3x3    p8est_quadrant_is_inside_3x3
+#define p4est_quadrant_is_outside_face  p8est_quadrant_is_outside_face
+#define p4est_quadrant_is_outside_corner p8est_quadrant_is_outside_corner
+#define p4est_quadrant_is_node          p8est_quadrant_is_node
+#define p4est_quadrant_is_valid         p8est_quadrant_is_valid
+#define p4est_quadrant_is_extended      p8est_quadrant_is_extended
+#define p4est_quadrant_is_sibling       p8est_quadrant_is_sibling
+#define p4est_quadrant_is_sibling_D     p8est_quadrant_is_sibling_D
+#define p4est_quadrant_is_family        p8est_quadrant_is_family
+#define p4est_quadrant_is_familyv       p8est_quadrant_is_familyv
+#define p4est_quadrant_is_familypv      p8est_quadrant_is_familypv
+#define p4est_quadrant_is_parent        p8est_quadrant_is_parent
+#define p4est_quadrant_is_parent_D      p8est_quadrant_is_parent_D
+#define p4est_quadrant_is_ancestor      p8est_quadrant_is_ancestor
+#define p4est_quadrant_is_ancestor_D    p8est_quadrant_is_ancestor_D
+#define p4est_quadrant_is_next          p8est_quadrant_is_next
+#define p4est_quadrant_is_next_D        p8est_quadrant_is_next_D
+#define p4est_quadrant_overlaps_tree    p8est_quadrant_overlaps_tree
+#define p4est_quadrant_is_inside_tree   p8est_quadrant_is_inside_tree
+#define p4est_quadrant_ancestor         p8est_quadrant_ancestor
+#define p4est_quadrant_parent           p8est_quadrant_parent
+#define p4est_quadrant_sibling          p8est_quadrant_sibling
+#define p4est_quadrant_face_neighbor    p8est_quadrant_face_neighbor
+#define p4est_quadrant_face_neighbor_extra p8est_quadrant_face_neighbor_extra
+#define p4est_quadrant_half_face_neighbors p8est_quadrant_half_face_neighbors
+#define p4est_quadrant_all_face_neighbors p8est_quadrant_all_face_neighbors
+#define p4est_quadrant_corner_neighbor  p8est_quadrant_corner_neighbor
+#define p4est_quadrant_corner_neighbor_extra \
+                                        p8est_quadrant_corner_neighbor_extra
+#define p4est_quadrant_half_corner_neighbor \
+                                        p8est_quadrant_half_corner_neighbor
+#define p4est_quadrant_corner_node      p8est_quadrant_corner_node
+#define p4est_quadrant_children         p8est_quadrant_children
+#define p4est_quadrant_childrenv        p8est_quadrant_childrenv
+#define p4est_quadrant_childrenpv       p8est_quadrant_childrenpv
+#define p4est_quadrant_first_descendant p8est_quadrant_first_descendant
+#define p4est_quadrant_last_descendant  p8est_quadrant_last_descendant
+#define p4est_quadrant_corner_descendant p8est_quadrant_corner_descendant
+#define p4est_nearest_common_ancestor   p8est_nearest_common_ancestor
+#define p4est_nearest_common_ancestor_D p8est_nearest_common_ancestor_D
+#define p4est_quadrant_transform_face   p8est_quadrant_transform_face
+#define p4est_quadrant_touches_corner   p8est_quadrant_touches_corner
+#define p4est_quadrant_transform_corner p8est_quadrant_transform_corner
+#define p4est_quadrant_shift_corner     p8est_quadrant_shift_corner
+#define p4est_quadrant_linear_id        p8est_quadrant_linear_id
+#define p4est_quadrant_set_morton       p8est_quadrant_set_morton
+
+/* functions in p4est_search */
+#define p4est_find_lower_bound          p8est_find_lower_bound
+#define p4est_find_higher_bound         p8est_find_higher_bound
+#define p4est_split_array               p8est_split_array
+#define p4est_find_range_boundaries     p8est_find_range_boundaries
+#define p4est_search                    p8est_search
+
+/* functions in p4est_algorithms */
+#define p4est_quadrant_init_data        p8est_quadrant_init_data
+#define p4est_quadrant_free_data        p8est_quadrant_free_data
+#define p4est_quadrant_checksum         p8est_quadrant_checksum
+#define p4est_tree_is_sorted            p8est_tree_is_sorted
+#define p4est_tree_is_linear            p8est_tree_is_linear
+#define p4est_tree_is_almost_sorted     p8est_tree_is_almost_sorted
+#define p4est_tree_is_complete          p8est_tree_is_complete
+#define p4est_tree_print                p8est_tree_print
+#define p4est_is_equal                  p8est_is_equal
+#define p4est_is_valid                  p8est_is_valid
+#define p4est_tree_compute_overlap      p8est_tree_compute_overlap
+#define p4est_tree_uniqify_overlap      p8est_tree_uniqify_overlap
+#define p4est_tree_remove_nonowned      p8est_tree_remove_nonowned
+#define p4est_complete_region           p8est_complete_region
+#define p4est_complete_subtree          p8est_complete_subtree
+#define p4est_balance_subtree           p8est_balance_subtree
+#define p4est_balance_border            p8est_balance_border
+#define p4est_linearize_tree            p8est_linearize_tree
+#define p4est_next_nonempty_process     p8est_next_nonempty_process
+#define p4est_partition_correction      p8est_partition_correction
+#define p4est_partition_for_coarsening  p8est_partition_for_coarsening
+#define p4est_partition_given           p8est_partition_given
+
+/* functions in p4est_communication */
+#define p4est_comm_count_quadrants      p8est_comm_count_quadrants
+#define p4est_comm_global_partition     p8est_comm_global_partition
+#define p4est_comm_count_pertree        p8est_comm_count_pertree
+#define p4est_comm_is_owner             p8est_comm_is_owner
+#define p4est_comm_find_owner           p8est_comm_find_owner
+#define p4est_comm_tree_info            p8est_comm_tree_info
+#define p4est_comm_neighborhood_owned   p8est_comm_neighborhood_owned
+#define p4est_comm_sync_flag            p8est_comm_sync_flag
+#define p4est_comm_checksum             p8est_comm_checksum
+
+/* functions in p4est_io */
+#define p4est_deflate_quadrants         p8est_deflate_quadrants
+#define p4est_inflate                   p8est_inflate
+
+/* functions in p4est_geometry */
+#define p4est_geometry_destroy          p8est_geometry_destroy
+#define p4est_geometry_new_connectivity p8est_geometry_new_connectivity
+
+/* functions in p4est_vtk */
+#define p4est_vtk_write_file            p8est_vtk_write_file
+#define p4est_vtk_write_all             p8est_vtk_write_all
+#define p4est_vtk_write_header          p8est_vtk_write_header
+#define p4est_vtk_write_point_scalar    p8est_vtk_write_point_scalar
+#define p4est_vtk_write_point_vector    p8est_vtk_write_point_vector
+#define p4est_vtk_write_footer          p8est_vtk_write_footer
+
+/* functions in p4est_ghost */
+#define p4est_quadrant_find_owner       p8est_quadrant_find_owner
+#define p4est_ghost_memory_used         p8est_ghost_memory_used
+#define p4est_ghost_new                 p8est_ghost_new
+#define p4est_ghost_destroy             p8est_ghost_destroy
+#define p4est_ghost_exchange_data       p8est_ghost_exchange_data
+#define p4est_ghost_exchange_custom     p8est_ghost_exchange_custom
+#define p4est_ghost_exchange_custom_levels p8est_ghost_exchange_custom_levels
+#define p4est_ghost_bsearch             p8est_ghost_bsearch
+#define p4est_ghost_contains            p8est_ghost_contains
+#define p4est_ghost_is_valid            p8est_ghost_is_valid
+#define p4est_face_quadrant_exists      p8est_face_quadrant_exists
+#define p4est_quadrant_exists           p8est_quadrant_exists
+#define p4est_is_balanced               p8est_is_balanced
+#define p4est_ghost_checksum            p8est_ghost_checksum
+#define p4est_ghost_expand              p8est_ghost_expand
+
+/* functions in p4est_nodes */
+#define p4est_nodes_new                 p8est_nodes_new
+#define p4est_nodes_destroy             p8est_nodes_destroy
+#define p4est_nodes_is_valid            p8est_nodes_is_valid
+
+/* functions in p4est_lnodes */
+#define p4est_lnodes_new                p8est_lnodes_new
+#define p4est_lnodes_destroy            p8est_lnodes_destroy
+#define p4est_ghost_support_lnodes      p8est_ghost_support_lnodes
+#define p4est_ghost_expand_by_lnodes    p8est_ghost_expand_by_lnodes
+#define p4est_lnodes_decode             p8est_lnodes_decode
+#define p4est_lnodes_share_owned_begin  p8est_lnodes_share_owned_begin
+#define p4est_lnodes_share_owned_end    p8est_lnodes_share_owned_end
+#define p4est_lnodes_share_owned        p8est_lnodes_share_owned
+#define p4est_lnodes_share_all_begin    p8est_lnodes_share_all_begin
+#define p4est_lnodes_share_all_end      p8est_lnodes_share_all_end
+#define p4est_lnodes_share_all          p8est_lnodes_share_all
+#define p4est_lnodes_buffer_destroy     p8est_lnodes_buffer_destroy
+#define p4est_lnodes_rank_array_index   p8est_lnodes_rank_array_index
+#define p4est_lnodes_rank_array_index_int p8est_lnodes_rank_array_index_int
+#define p4est_lnodes_global_index       p8est_lnodes_global_index
+
+/* functions in p4est_mesh */
+#define p4est_mesh_memory_used          p8est_mesh_memory_used
+#define p4est_mesh_new                  p8est_mesh_new
+#define p4est_mesh_destroy              p8est_mesh_destroy
+#define p4est_mesh_quadrant_cumulative  p8est_mesh_quadrant_cumulative
+#define p4est_mesh_face_neighbor_init   p8est_mesh_face_neighbor_init
+#define p4est_mesh_face_neighbor_init2  p8est_mesh_face_neighbor_init2
+#define p4est_mesh_face_neighbor_next   p8est_mesh_face_neighbor_next
+#define p4est_mesh_face_neighbor_data   p8est_mesh_face_neighbor_data
+
+/* functions in p4est_balance */
+#define p4est_balance_seeds_face        p8est_balance_seeds_face
+#define p4est_balance_seeds_corner      p8est_balance_seeds_corner
+#define p4est_balance_seeds             p8est_balance_seeds
+
+/* functions in p4est_wrap */
+#define p4est_wrap_new_conn             p8est_wrap_new_conn
+#define p4est_wrap_new_world            p8est_wrap_new_world
+#define p4est_wrap_destroy              p8est_wrap_destroy
+#define p4est_wrap_get_ghost            p8est_wrap_get_ghost
+#define p4est_wrap_get_mesh             p8est_wrap_get_mesh
+#define p4est_wrap_mark_refine          p8est_wrap_mark_refine
+#define p4est_wrap_mark_coarsen         p8est_wrap_mark_coarsen
+#define p4est_wrap_adapt                p8est_wrap_adapt
+#define p4est_wrap_partition            p8est_wrap_partition
+#define p4est_wrap_complete             p8est_wrap_complete
+#define p4est_wrap_leaf_next            p8est_wrap_leaf_next
+#define p4est_wrap_leaf_first           p8est_wrap_leaf_first
+
+/* functions in p4est_plex */
+#define p4est_get_plex_data             p8est_get_plex_data
+#endif /* !P4EST_TO_P8EST_H */
diff --git a/src/p4est_vtk.c b/src/p4est_vtk.c
new file mode 100644
index 0000000..b737eb7
--- /dev/null
+++ b/src/p4est_vtk.c
@@ -0,0 +1,949 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef P4_TO_P8
+#include <p8est_vtk.h>
+#include <p8est_nodes.h>
+#define P4EST_VTK_CELL_TYPE     11      /* VTK_VOXEL */
+#else
+#include <p4est_vtk.h>
+#include <p4est_nodes.h>
+#define P4EST_VTK_CELL_TYPE      8      /* VTK_PIXEL */
+#endif /* !P4_TO_P8 */
+
+#include <sc_io.h>
+
+static const double p4est_vtk_scale = 0.95;
+static const int    p4est_vtk_write_tree = 1;
+static const int    p4est_vtk_write_level = 1;
+static const int    p4est_vtk_write_rank = 1;
+static const int    p4est_vtk_wrap_rank = 0;
+
+#ifndef P4EST_VTK_DOUBLES
+#define P4EST_VTK_FLOAT_NAME "Float32"
+#define P4EST_VTK_FLOAT_TYPE float
+#else
+#define P4EST_VTK_FLOAT_NAME "Float64"
+#define P4EST_VTK_FLOAT_TYPE double
+#endif
+
+#ifndef P4EST_VTK_BINARY
+#define P4EST_VTK_ASCII 1
+#define P4EST_VTK_FORMAT_STRING "ascii"
+#else
+#define P4EST_VTK_FORMAT_STRING "binary"
+
+static int
+p4est_vtk_write_binary (FILE * vtkfile, char *numeric_data,
+                        size_t byte_length)
+{
+#ifndef P4EST_VTK_COMPRESSION
+  return sc_vtk_write_binary (vtkfile, numeric_data, byte_length);
+#else
+  return sc_vtk_write_compressed (vtkfile, numeric_data, byte_length);
+#endif /* P4EST_VTK_COMPRESSION */
+}
+
+#endif /* P4EST_VTK_BINARY */
+
+void
+p4est_vtk_write_file (p4est_t * p4est, p4est_geometry_t * geom,
+                      const char *filename)
+{
+  p4est_vtk_write_all (p4est, geom,
+                       p4est_vtk_scale,
+                       p4est_vtk_write_tree, p4est_vtk_write_level,
+                       p4est_vtk_write_rank, p4est_vtk_wrap_rank,
+                       0, 0, filename);
+}
+
+void
+p4est_vtk_write_all (p4est_t * p4est, p4est_geometry_t * geom,
+                     double scale,
+                     int write_tree, int write_level,
+                     int write_rank, int wrap_rank,
+                     int num_scalars, int num_vectors,
+                     const char *filename, ...)
+{
+  int                 retval;
+  int                 i, all;
+  int                 scalar_strlen, vector_strlen;
+  char                point_scalars[BUFSIZ], point_vectors[BUFSIZ];
+  const char         *name, **names;
+  double            **values;
+  va_list             ap;
+
+  P4EST_ASSERT (num_scalars >= 0 && num_vectors >= 0);
+
+  values = P4EST_ALLOC (double *, num_scalars + num_vectors);
+  names = P4EST_ALLOC (const char *, num_scalars + num_vectors);
+
+  va_start (ap, filename);
+  all = 0;
+  scalar_strlen = 0;
+  point_scalars[0] = '\0';
+  for (i = 0; i < num_scalars; ++all, ++i) {
+    name = names[all] = va_arg (ap, const char *);
+    retval = snprintf (point_scalars + scalar_strlen, BUFSIZ - scalar_strlen,
+                       "%s%s", i == 0 ? "" : ",", name);
+    SC_CHECK_ABORT (retval > 0,
+                    P4EST_STRING "_vtk: Error collecting point scalars");
+    scalar_strlen += retval;
+    values[all] = va_arg (ap, double *);
+  }
+  vector_strlen = 0;
+  point_vectors[0] = '\0';
+  for (i = 0; i < num_vectors; ++all, ++i) {
+    name = names[all] = va_arg (ap, const char *);
+    retval = snprintf (point_vectors + vector_strlen, BUFSIZ - vector_strlen,
+                       "%s%s", i == 0 ? "" : ",", name);
+    SC_CHECK_ABORT (retval > 0,
+                    P4EST_STRING "_vtk: Error collecting point vectors");
+    vector_strlen += retval;
+    values[all] = va_arg (ap, double *);
+  }
+  va_end (ap);
+
+  retval = p4est_vtk_write_header (p4est, geom, scale,
+                                   write_tree, write_level,
+                                   write_rank, wrap_rank,
+                                   num_scalars > 0 ? point_scalars : NULL,
+                                   num_vectors > 0 ? point_vectors : NULL,
+                                   filename);
+  SC_CHECK_ABORT (!retval, P4EST_STRING "_vtk: Error writing header");
+
+  all = 0;
+  for (i = 0; i < num_scalars; ++all, ++i) {
+    retval = p4est_vtk_write_point_scalar (p4est, geom, filename,
+                                           names[all], values[all]);
+    SC_CHECK_ABORT (!retval,
+                    P4EST_STRING "_vtk: Error writing point scalars");
+  }
+  for (i = 0; i < num_vectors; ++all, ++i) {
+    retval = p4est_vtk_write_point_vector (p4est, geom, filename,
+                                           names[all], values[all]);
+    SC_CHECK_ABORT (!retval,
+                    P4EST_STRING "_vtk: Error writing point vectors");
+  }
+
+  retval = p4est_vtk_write_footer (p4est, filename);
+  SC_CHECK_ABORT (!retval, P4EST_STRING "_vtk: Error writing footer");
+
+  P4EST_FREE (values);
+  P4EST_FREE (names);
+}
+
+int
+p4est_vtk_write_header (p4est_t * p4est, p4est_geometry_t * geom,
+                        double scale,
+                        int write_tree, int write_level,
+                        int write_rank, int wrap_rank,
+                        const char *point_scalars, const char *point_vectors,
+                        const char *filename)
+{
+  p4est_connectivity_t *connectivity = p4est->connectivity;
+  sc_array_t         *trees = p4est->trees;
+  const int           mpirank = p4est->mpirank;
+  const double        intsize = 1.0 / P4EST_ROOT_LEN;
+  const double       *v = connectivity->vertices;
+  const p4est_topidx_t first_local_tree = p4est->first_local_tree;
+  const p4est_topidx_t last_local_tree = p4est->last_local_tree;
+  const p4est_topidx_t *tree_to_vertex = connectivity->tree_to_vertex;
+  const p4est_locidx_t Ncells = p4est->local_num_quadrants;
+  const p4est_locidx_t Ncorners = P4EST_CHILDREN * Ncells;
+#ifdef P4EST_VTK_ASCII
+  double              wx, wy, wz;
+  p4est_locidx_t      sk;
+#else
+  int                 retval;
+  uint8_t            *uint8_data;
+  p4est_locidx_t     *locidx_data;
+#endif
+  int                 xi, yi, j, k;
+#ifdef P4_TO_P8
+  int                 zi;
+#endif
+  double              h2, eta_x, eta_y, eta_z = 0.;
+  double              xyz[3], XYZ[3];   /* 3 not P4EST_DIM */
+  size_t              num_quads, zz;
+  p4est_topidx_t      jt;
+  p4est_topidx_t      vt[P4EST_CHILDREN];
+  p4est_locidx_t      quad_count, Ntotal;
+  p4est_locidx_t      il;
+  P4EST_VTK_FLOAT_TYPE *float_data;
+  sc_array_t         *quadrants, *indeps;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *quad;
+  p4est_nodes_t      *nodes;
+  p4est_indep_t      *in;
+  char                vtufilename[BUFSIZ];
+  FILE               *vtufile;
+
+  SC_CHECK_ABORT (p4est->connectivity->num_vertices > 0,
+                  "Must provide connectivity with vertex information");
+
+  P4EST_ASSERT (0. <= scale && scale <= 1. && wrap_rank >= 0);
+  P4EST_ASSERT (v != NULL && tree_to_vertex != NULL);
+
+  if (scale < 1.) {
+    /* when we scale the quadrants we need each corner separately */
+    nodes = NULL;
+    indeps = NULL;
+    Ntotal = Ncorners;
+  }
+  else {
+    /* when scale == 1. we can reuse shared quadrant corners */
+    nodes = p4est_nodes_new (p4est, NULL);
+    indeps = &nodes->indep_nodes;
+    Ntotal = nodes->num_owned_indeps;
+    P4EST_ASSERT ((size_t) Ntotal == indeps->elem_count);
+  }
+
+  /* Have each proc write to its own file */
+  snprintf (vtufilename, BUFSIZ, "%s_%04d.vtu", filename, mpirank);
+  /* Use "w" for writing the initial part of the file.
+   * For further parts, use "r+" and fseek so write_compressed succeeds.
+   */
+  vtufile = fopen (vtufilename, "wb");
+  if (vtufile == NULL) {
+    P4EST_LERRORF ("Could not open %s for output\n", vtufilename);
+    return -1;
+  }
+
+  fprintf (vtufile, "<?xml version=\"1.0\"?>\n");
+  fprintf (vtufile, "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"");
+#if defined P4EST_VTK_BINARY && defined P4EST_VTK_COMPRESSION
+  fprintf (vtufile, " compressor=\"vtkZLibDataCompressor\"");
+#endif
+#ifdef SC_IS_BIGENDIAN
+  fprintf (vtufile, " byte_order=\"BigEndian\">\n");
+#else
+  fprintf (vtufile, " byte_order=\"LittleEndian\">\n");
+#endif
+  fprintf (vtufile, "  <UnstructuredGrid>\n");
+  fprintf (vtufile,
+           "    <Piece NumberOfPoints=\"%lld\" NumberOfCells=\"%lld\">\n",
+           (long long) Ntotal, (long long) Ncells);
+  fprintf (vtufile, "      <Points>\n");
+
+  float_data = P4EST_ALLOC (P4EST_VTK_FLOAT_TYPE, 3 * Ntotal);
+
+  /* write point position data */
+  fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"Position\""
+           " NumberOfComponents=\"3\" format=\"%s\">\n",
+           P4EST_VTK_FLOAT_NAME, P4EST_VTK_FORMAT_STRING);
+
+  if (nodes == NULL) {
+    /* loop over the trees */
+    for (jt = first_local_tree, quad_count = 0; jt <= last_local_tree; ++jt) {
+      tree = p4est_tree_array_index (trees, jt);
+      quadrants = &tree->quadrants;
+      num_quads = quadrants->elem_count;
+
+      /* retrieve corners of the tree */
+      for (k = 0; k < P4EST_CHILDREN; ++k)
+        vt[k] = tree_to_vertex[jt * P4EST_CHILDREN + k];
+
+      /* loop over the elements in tree and calculated vertex coordinates */
+      for (zz = 0; zz < num_quads; ++zz, ++quad_count) {
+        quad = p4est_quadrant_array_index (quadrants, zz);
+        h2 = .5 * intsize * P4EST_QUADRANT_LEN (quad->level);
+        k = 0;
+#ifdef P4_TO_P8
+        for (zi = 0; zi < 2; ++zi) {
+          eta_z = intsize * quad->z + h2 * (1. + (zi * 2 - 1) * scale);
+#endif
+          for (yi = 0; yi < 2; ++yi) {
+            eta_y = intsize * quad->y + h2 * (1. + (yi * 2 - 1) * scale);
+            for (xi = 0; xi < 2; ++xi) {
+              P4EST_ASSERT (0 <= k && k < P4EST_CHILDREN);
+              eta_x = intsize * quad->x + h2 * (1. + (xi * 2 - 1) * scale);
+              if (geom != NULL) {
+                xyz[0] = eta_x;
+                xyz[1] = eta_y;
+                xyz[2] = eta_z;
+                geom->X (geom, jt, xyz, XYZ);
+                for (j = 0; j < 3; ++j) {
+                  float_data[3 * (P4EST_CHILDREN * quad_count + k) + j] =
+                    (P4EST_VTK_FLOAT_TYPE) XYZ[j];
+                }
+              }
+              else {
+                for (j = 0; j < 3; ++j) {
+                  /* *INDENT-OFF* */
+                xyz[j] =
+            ((1. - eta_z) * ((1. - eta_y) * ((1. - eta_x) * v[3 * vt[0] + j] +
+                                                   eta_x  * v[3 * vt[1] + j]) +
+                                   eta_y  * ((1. - eta_x) * v[3 * vt[2] + j] +
+                                                   eta_x  * v[3 * vt[3] + j]))
+#ifdef P4_TO_P8
+             +     eta_z  * ((1. - eta_y) * ((1. - eta_x) * v[3 * vt[4] + j] +
+                                                   eta_x  * v[3 * vt[5] + j]) +
+                                   eta_y  * ((1. - eta_x) * v[3 * vt[6] + j] +
+                                                   eta_x  * v[3 * vt[7] + j]))
+#endif
+            );
+                  /* *INDENT-ON* */
+                  float_data[3 * (P4EST_CHILDREN * quad_count + k) + j] =
+                    (P4EST_VTK_FLOAT_TYPE) xyz[j];
+                }
+              }
+              ++k;
+            }
+          }
+#ifdef P4_TO_P8
+        }
+#endif
+        P4EST_ASSERT (k == P4EST_CHILDREN);
+      }
+    }
+    P4EST_ASSERT (P4EST_CHILDREN * quad_count == Ntotal);
+  }
+  else {
+    for (zz = 0; zz < indeps->elem_count; ++zz) {
+      in = (p4est_indep_t *) sc_array_index (indeps, zz);
+
+      /* retrieve corners of the tree */
+      jt = in->p.which_tree;
+      for (k = 0; k < P4EST_CHILDREN; ++k)
+        vt[k] = tree_to_vertex[jt * P4EST_CHILDREN + k];
+
+      /* calculate vertex coordinates */
+      eta_x = intsize * in->x;
+      eta_y = intsize * in->y;
+#ifdef P4_TO_P8
+      eta_z = intsize * in->z;
+#endif
+      if (geom != NULL) {
+        xyz[0] = eta_x;
+        xyz[1] = eta_y;
+        xyz[2] = eta_z;
+        geom->X (geom, jt, xyz, XYZ);
+        for (j = 0; j < 3; ++j) {
+          float_data[3 * zz + j] = (P4EST_VTK_FLOAT_TYPE) XYZ[j];
+        }
+      }
+      else {
+        for (j = 0; j < 3; ++j) {
+          /* *INDENT-OFF* */
+          xyz[j] =
+            ((1. - eta_z) * ((1. - eta_y) * ((1. - eta_x) * v[3 * vt[0] + j] +
+                                                   eta_x  * v[3 * vt[1] + j]) +
+                                   eta_y  * ((1. - eta_x) * v[3 * vt[2] + j] +
+                                                   eta_x  * v[3 * vt[3] + j]))
+  #ifdef P4_TO_P8
+             +     eta_z  * ((1. - eta_y) * ((1. - eta_x) * v[3 * vt[4] + j] +
+                                                   eta_x  * v[3 * vt[5] + j]) +
+                                   eta_y  * ((1. - eta_x) * v[3 * vt[6] + j] +
+                                                   eta_x  * v[3 * vt[7] + j]))
+  #endif
+            );
+          /* *INDENT-ON* */
+
+          float_data[3 * zz + j] = (P4EST_VTK_FLOAT_TYPE) xyz[j];
+        }
+      }
+    }
+  }
+
+#ifdef P4EST_VTK_ASCII
+  for (il = 0; il < Ntotal; ++il) {
+    wx = float_data[3 * il + 0];
+    wy = float_data[3 * il + 1];
+    wz = float_data[3 * il + 2];
+
+#ifdef P4EST_VTK_DOUBLES
+    fprintf (vtufile, "     %24.16e %24.16e %24.16e\n", wx, wy, wz);
+#else
+    fprintf (vtufile, "          %16.8e %16.8e %16.8e\n", wx, wy, wz);
+#endif
+  }
+#else
+  fprintf (vtufile, "          ");
+  /* TODO: Don't allocate the full size of the array, only allocate
+   * the chunk that will be passed to zlib and do this a chunk
+   * at a time.
+   */
+  retval = p4est_vtk_write_binary (vtufile, (char *) float_data,
+                                   sizeof (*float_data) * 3 * Ntotal);
+  fprintf (vtufile, "\n");
+  if (retval) {
+    P4EST_LERROR (P4EST_STRING "_vtk: Error encoding points\n");
+    fclose (vtufile);
+    return -1;
+  }
+#endif
+  P4EST_FREE (float_data);
+  fprintf (vtufile, "        </DataArray>\n");
+  fprintf (vtufile, "      </Points>\n");
+  fprintf (vtufile, "      <Cells>\n");
+
+  /* write connectivity data */
+  fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"connectivity\""
+           " format=\"%s\">\n", P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+  for (sk = 0, il = 0; il < Ncells; ++il) {
+    fprintf (vtufile, "         ");
+    for (k = 0; k < P4EST_CHILDREN; ++sk, ++k) {
+      fprintf (vtufile, " %lld", nodes == NULL ?
+               (long long) sk : (long long) nodes->local_nodes[sk]);
+    }
+    fprintf (vtufile, "\n");
+  }
+#else
+  locidx_data = P4EST_ALLOC (p4est_locidx_t, Ncorners);
+  fprintf (vtufile, "          ");
+  if (nodes == NULL) {
+    for (il = 0; il < Ncorners; ++il) {
+      locidx_data[il] = il;
+    }
+    retval =
+      p4est_vtk_write_binary (vtufile, (char *) locidx_data,
+                              sizeof (*locidx_data) * Ncorners);
+  }
+  else {
+    retval =
+      p4est_vtk_write_binary (vtufile, (char *) nodes->local_nodes,
+                              sizeof (*locidx_data) * Ncorners);
+  }
+  fprintf (vtufile, "\n");
+  if (retval) {
+    P4EST_LERROR (P4EST_STRING "_vtk: Error encoding connectivity\n");
+    fclose (vtufile);
+    return -1;
+  }
+#endif
+  fprintf (vtufile, "        </DataArray>\n");
+
+  /* write offset data */
+  fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"offsets\""
+           " format=\"%s\">\n", P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+  fprintf (vtufile, "         ");
+  for (il = 1, sk = 1; il <= Ncells; ++il, ++sk) {
+    fprintf (vtufile, " %lld", (long long) (P4EST_CHILDREN * il));
+    if (!(sk % 8) && il != Ncells)
+      fprintf (vtufile, "\n         ");
+  }
+  fprintf (vtufile, "\n");
+#else
+  for (il = 1; il <= Ncells; ++il)
+    locidx_data[il - 1] = P4EST_CHILDREN * il;  /* same type */
+
+  fprintf (vtufile, "          ");
+  retval = p4est_vtk_write_binary (vtufile, (char *) locidx_data,
+                                   sizeof (*locidx_data) * Ncells);
+  fprintf (vtufile, "\n");
+  if (retval) {
+    P4EST_LERROR (P4EST_STRING "_vtk: Error encoding offsets\n");
+    fclose (vtufile);
+    return -1;
+  }
+#endif
+  fprintf (vtufile, "        </DataArray>\n");
+
+  /* write type data */
+  fprintf (vtufile, "        <DataArray type=\"UInt8\" Name=\"types\""
+           " format=\"%s\">\n", P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+  fprintf (vtufile, "         ");
+  for (il = 0, sk = 1; il < Ncells; ++il, ++sk) {
+    fprintf (vtufile, " %d", P4EST_VTK_CELL_TYPE);
+    if (!(sk % 20) && il != (Ncells - 1))
+      fprintf (vtufile, "\n         ");
+  }
+  fprintf (vtufile, "\n");
+#else
+  uint8_data = P4EST_ALLOC (uint8_t, Ncells);
+  for (il = 0; il < Ncells; ++il)
+    uint8_data[il] = P4EST_VTK_CELL_TYPE;
+
+  fprintf (vtufile, "          ");
+  retval = p4est_vtk_write_binary (vtufile, (char *) uint8_data,
+                                   sizeof (*uint8_data) * Ncells);
+  fprintf (vtufile, "\n");
+  if (retval) {
+    P4EST_LERROR (P4EST_STRING "_vtk: Error encoding types\n");
+    fclose (vtufile);
+    return -1;
+  }
+#endif
+  fprintf (vtufile, "        </DataArray>\n");
+  fprintf (vtufile, "      </Cells>\n");
+
+  if (write_tree || write_level || write_rank) {
+    char                vtkCellDataString[BUFSIZ] = "";
+    int                 printed = 0;
+
+    if (write_tree)
+      printed +=
+        snprintf (vtkCellDataString + printed, BUFSIZ - printed, "treeid");
+    if (write_level)
+      printed +=
+        snprintf (vtkCellDataString + printed, BUFSIZ - printed,
+                  printed > 0 ? ",level" : "level");
+    if (write_rank)
+      printed +=
+        snprintf (vtkCellDataString + printed, BUFSIZ - printed,
+                  printed > 0 ? ",mpirank" : "mpirank");
+
+    fprintf (vtufile, "      <CellData Scalars=\"%s\">\n", vtkCellDataString);
+  }
+
+  if (write_tree) {
+    fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"treeid\""
+             " format=\"%s\">\n", P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+    fprintf (vtufile, "         ");
+    for (il = 0, sk = 1, jt = first_local_tree; jt <= last_local_tree; ++jt) {
+      tree = p4est_tree_array_index (trees, jt);
+      num_quads = tree->quadrants.elem_count;
+      for (zz = 0; zz < num_quads; ++zz, ++sk, ++il) {
+        fprintf (vtufile, " %lld", (long long) jt);
+        if (!(sk % 20) && il != (Ncells - 1))
+          fprintf (vtufile, "\n         ");
+      }
+    }
+    fprintf (vtufile, "\n");
+#else
+    for (il = 0, jt = first_local_tree; jt <= last_local_tree; ++jt) {
+      tree = p4est_tree_array_index (trees, jt);
+      num_quads = tree->quadrants.elem_count;
+      for (zz = 0; zz < num_quads; ++zz, ++il) {
+        locidx_data[il] = (p4est_locidx_t) jt;
+      }
+    }
+    fprintf (vtufile, "          ");
+    retval = p4est_vtk_write_binary (vtufile, (char *) locidx_data,
+                                     sizeof (*locidx_data) * Ncells);
+    fprintf (vtufile, "\n");
+    if (retval) {
+      P4EST_LERROR (P4EST_STRING "_vtk: Error encoding types\n");
+      fclose (vtufile);
+      return -1;
+    }
+#endif
+    fprintf (vtufile, "        </DataArray>\n");
+    P4EST_ASSERT (il == Ncells);
+  }
+
+  if (write_level) {
+    fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"level\""
+             " format=\"%s\">\n", "UInt8", P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+    fprintf (vtufile, "         ");
+    for (il = 0, sk = 1, jt = first_local_tree; jt <= last_local_tree; ++jt) {
+      tree = p4est_tree_array_index (trees, jt);
+      quadrants = &tree->quadrants;
+      num_quads = quadrants->elem_count;
+      for (zz = 0; zz < num_quads; ++zz, ++sk, ++il) {
+        quad = p4est_quadrant_array_index (quadrants, zz);
+        fprintf (vtufile, " %d", (int) quad->level);
+        if (!(sk % 20) && il != (Ncells - 1))
+          fprintf (vtufile, "\n         ");
+      }
+    }
+    fprintf (vtufile, "\n");
+#else
+    for (il = 0, jt = first_local_tree; jt <= last_local_tree; ++jt) {
+      tree = p4est_tree_array_index (trees, jt);
+      quadrants = &tree->quadrants;
+      num_quads = quadrants->elem_count;
+      for (zz = 0; zz < num_quads; ++zz, ++il) {
+        quad = p4est_quadrant_array_index (quadrants, zz);
+        uint8_data[il] = (uint8_t) quad->level;
+      }
+    }
+
+    fprintf (vtufile, "          ");
+    retval = p4est_vtk_write_binary (vtufile, (char *) uint8_data,
+                                     sizeof (*uint8_data) * Ncells);
+    fprintf (vtufile, "\n");
+    if (retval) {
+      P4EST_LERROR (P4EST_STRING "_vtk: Error encoding types\n");
+      fclose (vtufile);
+      return -1;
+    }
+#endif
+    fprintf (vtufile, "        </DataArray>\n");
+  }
+
+  if (write_rank) {
+    const int           wrapped_rank =
+      wrap_rank > 0 ? mpirank % wrap_rank : mpirank;
+
+    fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"mpirank\""
+             " format=\"%s\">\n", P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+#ifdef P4EST_VTK_ASCII
+    fprintf (vtufile, "         ");
+    for (il = 0, sk = 1; il < Ncells; ++il, ++sk) {
+      fprintf (vtufile, " %d", wrapped_rank);
+      if (!(sk % 20) && il != (Ncells - 1))
+        fprintf (vtufile, "\n         ");
+    }
+    fprintf (vtufile, "\n");
+#else
+    for (il = 0; il < Ncells; ++il)
+      locidx_data[il] = (p4est_locidx_t) wrapped_rank;
+
+    fprintf (vtufile, "          ");
+    retval = p4est_vtk_write_binary (vtufile, (char *) locidx_data,
+                                     sizeof (*locidx_data) * Ncells);
+    fprintf (vtufile, "\n");
+    if (retval) {
+      P4EST_LERROR (P4EST_STRING "_vtk: Error encoding types\n");
+      fclose (vtufile);
+      return -1;
+    }
+#endif
+    fprintf (vtufile, "        </DataArray>\n");
+  }
+
+  if (write_tree || write_level || write_rank) {
+    fprintf (vtufile, "      </CellData>\n");
+  }
+
+#ifndef P4EST_VTK_ASCII
+  P4EST_FREE (locidx_data);
+  P4EST_FREE (uint8_data);
+#endif
+
+  if (nodes != NULL) {
+    p4est_nodes_destroy (nodes);
+  }
+
+  fprintf (vtufile, "      <PointData");
+  if (point_scalars != NULL)
+    fprintf (vtufile, " Scalars=\"%s\"", point_scalars);
+  if (point_vectors != NULL)
+    fprintf (vtufile, " Vectors=\"%s\"", point_vectors);
+  fprintf (vtufile, ">\n");
+
+  if (ferror (vtufile)) {
+    P4EST_LERROR (P4EST_STRING "_vtk: Error writing header\n");
+    fclose (vtufile);
+    return -1;
+  }
+  if (fclose (vtufile)) {
+    P4EST_LERROR (P4EST_STRING "_vtk: Error closing header\n");
+    return -1;
+  }
+  vtufile = NULL;
+
+  /* Only have the root write to the parallel vtk file */
+  if (mpirank == 0) {
+    char                pvtufilename[BUFSIZ];
+    FILE               *pvtufile;
+
+    snprintf (pvtufilename, BUFSIZ, "%s.pvtu", filename);
+
+    pvtufile = fopen (pvtufilename, "wb");
+    if (!pvtufile) {
+      P4EST_LERRORF ("Could not open %s for output\n", vtufilename);
+      return -1;
+    }
+
+    fprintf (pvtufile, "<?xml version=\"1.0\"?>\n");
+    fprintf (pvtufile, "<VTKFile type=\"PUnstructuredGrid\" version=\"0.1\"");
+#if defined P4EST_VTK_BINARY && defined P4EST_VTK_COMPRESSION
+    fprintf (pvtufile, " compressor=\"vtkZLibDataCompressor\"");
+#endif
+#ifdef SC_IS_BIGENDIAN
+    fprintf (pvtufile, " byte_order=\"BigEndian\">\n");
+#else
+    fprintf (pvtufile, " byte_order=\"LittleEndian\">\n");
+#endif
+
+    fprintf (pvtufile, "  <PUnstructuredGrid GhostLevel=\"0\">\n");
+    fprintf (pvtufile, "    <PPoints>\n");
+    fprintf (pvtufile, "      <PDataArray type=\"%s\" Name=\"Position\""
+             " NumberOfComponents=\"3\" format=\"%s\"/>\n",
+             P4EST_VTK_FLOAT_NAME, P4EST_VTK_FORMAT_STRING);
+    fprintf (pvtufile, "    </PPoints>\n");
+    if (write_tree || write_level || write_rank) {
+      char                vtkCellDataString[BUFSIZ] = "";
+      int                 printed = 0;
+
+      if (write_tree)
+        printed +=
+          snprintf (vtkCellDataString + printed, BUFSIZ - printed, "treeid");
+      if (write_level)
+        printed +=
+          snprintf (vtkCellDataString + printed, BUFSIZ - printed,
+                    printed > 0 ? ",level" : "level");
+      if (write_rank)
+        printed +=
+          snprintf (vtkCellDataString + printed, BUFSIZ - printed,
+                    printed > 0 ? ",mpirank" : "mpirank");
+
+      fprintf (pvtufile, "    <PCellData Scalars=\"%s\">\n",
+               vtkCellDataString);
+    }
+    if (write_tree) {
+      fprintf (pvtufile, "      "
+               "<PDataArray type=\"%s\" Name=\"treeid\" format=\"%s\"/>\n",
+               P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+    }
+    if (write_level) {
+      fprintf (pvtufile, "      "
+               "<PDataArray type=\"%s\" Name=\"level\" format=\"%s\"/>\n",
+               "UInt8", P4EST_VTK_FORMAT_STRING);
+    }
+    if (write_rank) {
+      fprintf (pvtufile, "      "
+               "<PDataArray type=\"%s\" Name=\"mpirank\" format=\"%s\"/>\n",
+               P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING);
+    }
+    if (write_tree || write_level || write_rank) {
+      fprintf (pvtufile, "    </PCellData>\n");
+    }
+    fprintf (pvtufile, "    <PPointData>\n");
+
+    if (ferror (pvtufile)) {
+      P4EST_LERROR (P4EST_STRING "_vtk: Error writing parallel header\n");
+      fclose (pvtufile);
+      return -1;
+    }
+    if (fclose (pvtufile)) {
+      P4EST_LERROR (P4EST_STRING "_vtk: Error closing parallel header\n");
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+int
+p4est_vtk_write_point_scalar (p4est_t * p4est, p4est_geometry_t * geom,
+                              const char *filename,
+                              const char *scalar_name, const double *values)
+{
+  const int           mpirank = p4est->mpirank;
+  const p4est_locidx_t Ncells = p4est->local_num_quadrants;
+  const p4est_locidx_t Ncorners = P4EST_CHILDREN * Ncells;      /* type ok */
+  int                 retval;
+  p4est_locidx_t      il;
+#ifndef P4EST_VTK_ASCII
+  P4EST_VTK_FLOAT_TYPE *float_data;
+#endif
+  char                vtufilename[BUFSIZ];
+  FILE               *vtufile;
+
+  /* Have each proc write to its own file */
+  snprintf (vtufilename, BUFSIZ, "%s_%04d.vtu", filename, mpirank);
+  /* To be able to fseek in a file you cannot open in append mode.
+   * so you need to open with "r+" and fseek to SEEK_END.
+   */
+  vtufile = fopen (vtufilename, "rb+");
+  if (vtufile == NULL) {
+    P4EST_LERRORF ("Could not open %s for output\n", vtufilename);
+    return -1;
+  }
+  retval = fseek (vtufile, 0L, SEEK_END);
+  if (retval) {
+    P4EST_LERRORF ("Could not fseek %s for output\n", vtufilename);
+    fclose (vtufile);
+    return -1;
+  }
+
+  /* write point position data */
+  fprintf (vtufile, "        <DataArray type=\"%s\" Name=\"%s\""
+           " format=\"%s\">\n",
+           P4EST_VTK_FLOAT_NAME, scalar_name, P4EST_VTK_FORMAT_STRING);
+
+#ifdef P4EST_VTK_ASCII
+  for (il = 0; il < Ncorners; ++il) {
+#ifdef P4EST_VTK_DOUBLES
+    fprintf (vtufile, "     %24.16e\n", values[il]);
+#else
+    fprintf (vtufile, "          %16.8e\n", values[il]);
+#endif
+  }
+#else
+  float_data = P4EST_ALLOC (P4EST_VTK_FLOAT_TYPE, Ncorners);
+  for (il = 0; il < Ncorners; ++il) {
+    float_data[il] = (P4EST_VTK_FLOAT_TYPE) values[il];
+  }
+
+  fprintf (vtufile, "          ");
+  /* TODO: Don't allocate the full size of the array, only allocate
+   * the chunk that will be passed to zlib and do this a chunk
+   * at a time.
+   */
+  retval = p4est_vtk_write_binary (vtufile, (char *) float_data,
+                                   sizeof (*float_data) * Ncorners);
+  fprintf (vtufile, "\n");
+  if (retval) {
+    P4EST_LERROR (P4EST_STRING "_vtk: Error encoding points\n");
+    fclose (vtufile);
+    return -1;
+  }
+  P4EST_FREE (float_data);
+#endif
+  fprintf (vtufile, "        </DataArray>\n");
+
+  if (ferror (vtufile)) {
+    P4EST_LERROR (P4EST_STRING "_vtk: Error writing point scalar\n");
+    fclose (vtufile);
+    return -1;
+  }
+  if (fclose (vtufile)) {
+    P4EST_LERROR (P4EST_STRING "_vtk: Error closing point scalar\n");
+    return -1;
+  }
+  vtufile = NULL;
+
+  /* Only have the root write to the parallel vtk file */
+  if (mpirank == 0) {
+    char                pvtufilename[BUFSIZ];
+    FILE               *pvtufile;
+    snprintf (pvtufilename, BUFSIZ, "%s.pvtu", filename);
+
+    pvtufile = fopen (pvtufilename, "ab");
+    if (!pvtufile) {
+      P4EST_LERRORF ("Could not open %s for output\n", vtufilename);
+      return -1;
+    }
+
+    fprintf (pvtufile, "      <PDataArray type=\"%s\" Name=\"%s\""
+             " format=\"%s\"/>\n",
+             P4EST_VTK_FLOAT_NAME, scalar_name, P4EST_VTK_FORMAT_STRING);
+
+    if (ferror (pvtufile)) {
+      P4EST_LERROR (P4EST_STRING
+                    "_vtk: Error writing parallel point scalar\n");
+      fclose (pvtufile);
+      return -1;
+    }
+    if (fclose (pvtufile)) {
+      P4EST_LERROR (P4EST_STRING
+                    "_vtk: Error closing parallel point scalar\n");
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+int
+p4est_vtk_write_point_vector (p4est_t * p4est, p4est_geometry_t * geom,
+                              const char *filename,
+                              const char *vector_name, const double *values)
+{
+  SC_ABORT (P4EST_STRING "_vtk_write_point_vector not implemented");
+}
+
+int
+p4est_vtk_write_footer (p4est_t * p4est, const char *filename)
+{
+  char                vtufilename[BUFSIZ];
+  int                 p;
+  int                 procRank = p4est->mpirank;
+  int                 numProcs = p4est->mpisize;
+  FILE               *vtufile;
+
+  /* Have each proc write to its own file */
+  snprintf (vtufilename, BUFSIZ, "%s_%04d.vtu", filename, procRank);
+  vtufile = fopen (vtufilename, "ab");
+  if (vtufile == NULL) {
+    P4EST_LERRORF ("Could not open %s for output!\n", vtufilename);
+    return -1;
+  }
+
+  fprintf (vtufile, "      </PointData>\n");
+  fprintf (vtufile, "    </Piece>\n");
+  fprintf (vtufile, "  </UnstructuredGrid>\n");
+  fprintf (vtufile, "</VTKFile>\n");
+
+  if (ferror (vtufile)) {
+    P4EST_LERROR ("p4est_vtk: Error writing footer\n");
+    fclose (vtufile);
+    return -1;
+  }
+  if (fclose (vtufile)) {
+    P4EST_LERROR ("p4est_vtk: Error closing footer\n");
+    return -1;
+  }
+  vtufile = NULL;
+
+  /* Only have the root write to the parallel vtk file */
+  if (procRank == 0) {
+    char                visitfilename[BUFSIZ];
+    char                pvtufilename[BUFSIZ];
+    FILE               *pvtufile, *visitfile;
+
+    /* Reopen paraview master file for writing bottom half */
+    snprintf (pvtufilename, BUFSIZ, "%s.pvtu", filename);
+    pvtufile = fopen (pvtufilename, "ab");
+    if (!pvtufile) {
+      P4EST_LERRORF ("Could not open %s for output!\n", vtufilename);
+      return -1;
+    }
+
+    /* Create a master file for visualization in Visit */
+    snprintf (visitfilename, BUFSIZ, "%s.visit", filename);
+    visitfile = fopen (visitfilename, "wb");
+    if (!visitfile) {
+      P4EST_LERRORF ("Could not open %s for output\n", visitfilename);
+      fclose (pvtufile);
+      return -1;
+    }
+    fprintf (visitfile, "!NBLOCKS %d\n", numProcs);
+
+    /* Write data about the parallel pieces into both files */
+    fprintf (pvtufile, "    </PPointData>\n");
+    for (p = 0; p < numProcs; ++p) {
+      fprintf (pvtufile,
+               "    <Piece Source=\"%s_%04d.vtu\"/>\n", filename, p);
+      fprintf (visitfile, "%s_%04d.vtu\n", filename, p);
+    }
+    fprintf (pvtufile, "  </PUnstructuredGrid>\n");
+    fprintf (pvtufile, "</VTKFile>\n");
+
+    /* Close paraview master file */
+    if (ferror (pvtufile)) {
+      P4EST_LERROR ("p4est_vtk: Error writing parallel footer\n");
+      fclose (visitfile);
+      fclose (pvtufile);
+      return -1;
+    }
+    if (fclose (pvtufile)) {
+      fclose (visitfile);
+      P4EST_LERROR ("p4est_vtk: Error closing parallel footer\n");
+      return -1;
+    }
+
+    /* Close visit master file */
+    if (ferror (visitfile)) {
+      P4EST_LERROR ("p4est_vtk: Error writing parallel footer\n");
+      fclose (visitfile);
+      return -1;
+    }
+    if (fclose (visitfile)) {
+      P4EST_LERROR ("p4est_vtk: Error closing parallel footer\n");
+      return -1;
+    }
+  }
+
+  return 0;
+}
diff --git a/src/p4est_vtk.h b/src/p4est_vtk.h
new file mode 100644
index 0000000..f86a72d
--- /dev/null
+++ b/src/p4est_vtk.h
@@ -0,0 +1,195 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p4est_vtk.h
+ *
+ * Routines for printing a forest and associated fields to vtk format.
+ *
+ * \ingroup p4est
+ */
+
+#ifndef P4EST_VTK_H
+#define P4EST_VTK_H
+
+#include <p4est_geometry.h>
+#include <p4est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** This writes out the p4est in VTK format.
+ *
+ * This is a convenience function for the special
+ * case of writing out the tree id, quadrant level, and MPI rank only.
+ * One file is written per MPI rank, and one meta file on rank 0.
+ * This function will abort if there is a file error.
+ *
+ * \param [in] p4est    The p4est to be written.
+ * \param [in] geom     A p4est_geometry_t structure or NULL for vertex space.
+ * \param [in] filename The first part of the file name which will have the
+ *                      MPI rank appended to it: The output file will be
+ *                      filename_rank.vtu, and the meta file filename.pvtu).
+ */
+void                p4est_vtk_write_file (p4est_t * p4est,
+                                          p4est_geometry_t * geom,
+                                          const char *filename);
+
+/** This writes out the p4est and any number of point fields in VTK format.
+ *
+ * This is a convenience function that will abort if there is a file error.
+ *
+ * \param [in] p4est    The p4est to be written.
+ * \param [in] geom     A p4est_geometry_t structure or NULL for vertex space.
+ * \param [in] scale    Double value between 0 and 1 to scale each quadrant.
+ * \param [in] write_tree   Include the tree id as output field.
+ * \param [in] write_level  Include the tree levels as output field.
+ * \param [in] write_rank   Include the MPI rank as output field.
+ * \param [in] wrap_tree    The MPI rank is written module wrap_tree, or 0.
+ * \param filename      First part of the name, see p4est_vtk_write_file.
+ * \param num_scalars   Number of scalar fields to write.
+ * \param num_vectors   Number of vector fields to write.
+ *
+ * The variable arguments need to be pairs of (fieldname, fieldvalues)
+ * where the scalars come first, then the vectors.
+ */
+void                p4est_vtk_write_all (p4est_t * p4est,
+                                         p4est_geometry_t * geom,
+                                         double scale,
+                                         int write_tree, int write_level,
+                                         int write_rank, int wrap_rank,
+                                         int num_scalars, int num_vectors,
+                                         const char *filename, ...);
+
+/** This will write the header of the vtu file.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of
+ * fields.  The calling sequence would be something like
+ *
+ * \begincode
+ * p4est_vtk_write_header(p4est, geom, 1., 1, 1, 0, "output");
+ * p4est_vtk_write_point_scalar (...);
+ * ...
+ * p4est_vtk_write_footer(p4est, "output");
+ * \endcode
+ *
+ * \param p4est     The p4est to be written.
+ * \param geom      A p4est_geometry_t structure or NULL for vertex space.
+ * \param scale     The relative length factor of the quadrants.
+ *                  Use 1.0 to fit quadrants exactly, less to create gaps.
+ * \param write_tree    Boolean to determine if the tree id should be output.
+ * \param write_level   Boolean to determine if the tree levels should be output.
+ * \param write_rank    Boolean to determine if the MPI rank should be output.
+ * \param wrap_rank Number to wrap around the rank with a modulo operation.
+ *                  Can be 0 for no wrapping.
+ * \param point_scalars  Comma-separated list of point scalar fields, or NULL.
+ * \param point_vectors  Comma-separated list of point vector fields, or NULL.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p4est_vtk_write_header (p4est_t * p4est,
+                                            p4est_geometry_t * geom,
+                                            double scale,
+                                            int write_tree, int write_level,
+                                            int write_rank, int wrap_rank,
+                                            const char *point_scalars,
+                                            const char *point_vectors,
+                                            const char *filename);
+
+/** This will write a scalar field to the vtu file.
+ *
+ * It is good practice to make sure that the scalar field also
+ * exists in the comma separated string \a point_scalars passed
+ * to \c p4est_vtk_write_header.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of fields.
+ *
+ * \param p4est     The p4est to be written.
+ * \param geom      A p4est_geometry_t structure or NULL for vertex space.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ * \param scalar_name The name of the scalar field.
+ * \param values    The point values that will be written.
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p4est_vtk_write_point_scalar (p4est_t * p4est,
+                                                  p4est_geometry_t * geom,
+                                                  const char *filename,
+                                                  const char *scalar_name,
+                                                  const double *values);
+
+/** This will write a 3-vector field to the vtu file.
+ *
+ * It is good practice to make sure that the vector field also
+ * exists in the comma separated string \a point_vectors passed
+ * to \c p4est_vtk_write_header.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of fields.
+ *
+ * \param p4est     The p4est to be written.
+ * \param geom      A p4est_geometry_t structure or NULL for vertex space.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ * \param vector_name The name of the vector field.
+ * \param values    The point values that will be written.
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p4est_vtk_write_point_vector (p4est_t * p4est,
+                                                  p4est_geometry_t * geom,
+                                                  const char *filename,
+                                                  const char *vector_name,
+                                                  const double *values);
+
+/** This will write the footer of the vtu file.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of
+ * fields.  To write out two fields the
+ * calling sequence would be something like
+ *
+ * \begincode
+ * p4est_vtk_write_header(p4est, ..., "output");
+ * p4est_vtk_write_footer(p4est, "output");
+ * \endcode
+ *
+ * \param p4est     The p4est to be written.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p4est_vtk_write_footer (p4est_t * p4est,
+                                            const char *filename);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_VTK_H */
diff --git a/src/p4est_wrap.c b/src/p4est_wrap.c
new file mode 100644
index 0000000..d0a8b9b
--- /dev/null
+++ b/src/p4est_wrap.c
@@ -0,0 +1,542 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_wrap.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_wrap.h>
+#endif
+
+static int
+refine_callback (p4est_t * p4est, p4est_topidx_t which_tree,
+                 p4est_quadrant_t * q)
+{
+  p4est_wrap_t       *pp = (p4est_wrap_t *) p4est->user_pointer;
+  const p4est_locidx_t old_counter = pp->inside_counter++;
+  const uint8_t       flag = pp->flags[old_counter];
+
+  P4EST_ASSERT (0 <= old_counter);
+  P4EST_ASSERT (0 <= pp->num_replaced
+                && pp->num_replaced <= pp->num_refine_flags);
+
+  /* copy current flag since we cannot be certain that refinement occurs */
+  pp->flags[old_counter] = 0;
+  pp->temp_flags[old_counter + (P4EST_CHILDREN - 1) * pp->num_replaced] =
+    flag & ~P4EST_WRAP_REFINE;
+
+  return flag & P4EST_WRAP_REFINE;
+}
+
+static void
+replace_on_refine (p4est_t * p4est, p4est_topidx_t which_tree,
+                   int num_outgoing, p4est_quadrant_t * outgoing[],
+                   int num_incoming, p4est_quadrant_t * incoming[])
+{
+  p4est_wrap_t       *pp = (p4est_wrap_t *) p4est->user_pointer;
+  const p4est_locidx_t new_counter =
+    pp->inside_counter - 1 + (P4EST_CHILDREN - 1) * pp->num_replaced++;
+  const uint8_t       flag = pp->temp_flags[new_counter];
+  int                 k;
+
+  /* this function is only called when refinement actually happens */
+  P4EST_ASSERT (num_outgoing == 1 && num_incoming == P4EST_CHILDREN);
+  P4EST_ASSERT (!(flag & (P4EST_WRAP_REFINE | P4EST_WRAP_COARSEN)));
+
+  for (k = 1; k < P4EST_CHILDREN; ++k) {
+    pp->temp_flags[new_counter + k] = flag;
+  }
+}
+
+static int
+coarsen_callback (p4est_t * p4est, p4est_topidx_t which_tree,
+                  p4est_quadrant_t * q[])
+{
+  p4est_wrap_t       *pp = (p4est_wrap_t *) p4est->user_pointer;
+  const p4est_locidx_t old_counter = pp->inside_counter++;
+  int                 k;
+
+  /* are we not coarsening at all, just counting? */
+  if (q[1] == NULL) {
+    return 0;
+  }
+
+  /* now we are possibly coarsening */
+  for (k = 0; k < P4EST_CHILDREN; ++k) {
+    if (!(pp->temp_flags[old_counter + k] & P4EST_WRAP_COARSEN)) {
+      return 0;
+    }
+  }
+
+  /* we are definitely coarsening */
+  pp->inside_counter += P4EST_CHILDREN - 1;
+  ++pp->num_replaced;
+  return 1;
+}
+
+p4est_wrap_t       *
+p4est_wrap_new_conn (sc_MPI_Comm mpicomm, p4est_connectivity_t * conn,
+                     int initial_level)
+{
+  p4est_wrap_t       *pp;
+
+  pp = P4EST_ALLOC (p4est_wrap_t, 1);
+  pp->user_pointer = NULL;
+
+  pp->p4est_dim = P4EST_DIM;
+  pp->p4est_half = P4EST_HALF;
+  pp->p4est_faces = P4EST_FACES;
+  pp->p4est_children = P4EST_CHILDREN;
+  pp->conn = conn;
+  pp->p4est = p4est_new_ext (mpicomm, pp->conn,
+                             0, initial_level, 1, 0, NULL, NULL);
+  pp->weight_exponent = 0;
+  pp->flags = P4EST_ALLOC_ZERO (uint8_t, pp->p4est->local_num_quadrants);
+  pp->temp_flags = NULL;
+  pp->num_refine_flags = pp->inside_counter = pp->num_replaced = 0;
+
+  pp->ghost = p4est_ghost_new (pp->p4est, P4EST_CONNECT_FULL);
+  pp->mesh = p4est_mesh_new_ext (pp->p4est, pp->ghost, 1, 1,
+                                 P4EST_CONNECT_FULL);
+
+  pp->ghost_aux = NULL;
+  pp->mesh_aux = NULL;
+  pp->match_aux = 0;
+
+  pp->p4est->user_pointer = pp;
+
+  return pp;
+}
+
+#ifndef P4_TO_P8
+
+p4est_wrap_t       *
+p4est_wrap_new_unitsquare (sc_MPI_Comm mpicomm, int initial_level)
+{
+  return p4est_wrap_new_conn (mpicomm,
+                              p4est_connectivity_new_unitsquare (),
+                              initial_level);
+}
+
+p4est_wrap_t       *
+p4est_wrap_new_periodic (sc_MPI_Comm mpicomm, int initial_level)
+{
+  return p4est_wrap_new_conn (mpicomm,
+                              p4est_connectivity_new_periodic (),
+                              initial_level);
+}
+
+p4est_wrap_t       *
+p4est_wrap_new_rotwrap (sc_MPI_Comm mpicomm, int initial_level)
+{
+  return p4est_wrap_new_conn (mpicomm,
+                              p4est_connectivity_new_rotwrap (),
+                              initial_level);
+}
+
+p4est_wrap_t       *
+p4est_wrap_new_corner (sc_MPI_Comm mpicomm, int initial_level)
+{
+  return p4est_wrap_new_conn (mpicomm,
+                              p4est_connectivity_new_corner (),
+                              initial_level);
+}
+
+p4est_wrap_t       *
+p4est_wrap_new_pillow (sc_MPI_Comm mpicomm, int initial_level)
+{
+  return p4est_wrap_new_conn (mpicomm,
+                              p4est_connectivity_new_pillow (),
+                              initial_level);
+}
+
+p4est_wrap_t       *
+p4est_wrap_new_moebius (sc_MPI_Comm mpicomm, int initial_level)
+{
+  return p4est_wrap_new_conn (mpicomm,
+                              p4est_connectivity_new_moebius (),
+                              initial_level);
+}
+
+p4est_wrap_t       *
+p4est_wrap_new_cubed (sc_MPI_Comm mpicomm, int initial_level)
+{
+  return p4est_wrap_new_conn (mpicomm,
+                              p4est_connectivity_new_cubed (), initial_level);
+}
+
+p4est_wrap_t       *
+p4est_wrap_new_disk (sc_MPI_Comm mpicomm, int initial_level)
+{
+  return p4est_wrap_new_conn (mpicomm,
+                              p4est_connectivity_new_disk (), initial_level);
+}
+
+#else
+
+p8est_wrap_t       *
+p8est_wrap_new_unitcube (sc_MPI_Comm mpicomm, int initial_level)
+{
+  return p4est_wrap_new_conn (mpicomm,
+                              p8est_connectivity_new_unitcube (),
+                              initial_level);
+}
+
+p8est_wrap_t       *
+p8est_wrap_new_rotwrap (sc_MPI_Comm mpicomm, int initial_level)
+{
+  return p4est_wrap_new_conn (mpicomm,
+                              p8est_connectivity_new_rotwrap (),
+                              initial_level);
+}
+
+#endif
+
+p4est_wrap_t       *
+p4est_wrap_new_world (int initial_level)
+{
+#ifndef P4_TO_P8
+  return p4est_wrap_new_unitsquare (sc_MPI_COMM_WORLD, initial_level);
+#else
+  return p8est_wrap_new_unitcube (sc_MPI_COMM_WORLD, initial_level);
+#endif
+}
+
+void
+p4est_wrap_destroy (p4est_wrap_t * pp)
+{
+  if (pp->mesh_aux != NULL) {
+    p4est_mesh_destroy (pp->mesh_aux);
+  }
+  if (pp->ghost_aux != NULL) {
+    p4est_ghost_destroy (pp->ghost_aux);
+  }
+
+  p4est_mesh_destroy (pp->mesh);
+  p4est_ghost_destroy (pp->ghost);
+
+  P4EST_FREE (pp->flags);
+  P4EST_FREE (pp->temp_flags);
+
+  p4est_destroy (pp->p4est);
+  p4est_connectivity_destroy (pp->conn);
+
+  P4EST_FREE (pp);
+}
+
+p4est_ghost_t      *
+p4est_wrap_get_ghost (p4est_wrap_t * pp)
+{
+  return pp->match_aux ? pp->ghost_aux : pp->ghost;
+}
+
+p4est_mesh_t       *
+p4est_wrap_get_mesh (p4est_wrap_t * pp)
+{
+  return pp->match_aux ? pp->mesh_aux : pp->mesh;
+}
+
+void
+p4est_wrap_mark_refine (p4est_wrap_t * pp,
+                        p4est_topidx_t which_tree, p4est_locidx_t which_quad)
+{
+  p4est_t            *p4est = pp->p4est;
+  p4est_tree_t       *tree;
+  p4est_locidx_t      pos;
+  uint8_t             flag;
+
+  P4EST_ASSERT (p4est->first_local_tree <= which_tree);
+  P4EST_ASSERT (which_tree <= p4est->last_local_tree);
+
+  tree = p4est_tree_array_index (p4est->trees, which_tree);
+  P4EST_ASSERT (0 <= which_quad &&
+                which_quad < (p4est_locidx_t) tree->quadrants.elem_count);
+  pos = tree->quadrants_offset + which_quad;
+  P4EST_ASSERT (0 <= pos && pos < p4est->local_num_quadrants);
+
+  flag = pp->flags[pos];
+  if (!(flag & P4EST_WRAP_REFINE)) {
+    pp->flags[pos] |= P4EST_WRAP_REFINE;
+    ++pp->num_refine_flags;
+  }
+  pp->flags[pos] &= ~P4EST_WRAP_COARSEN;
+}
+
+void
+p4est_wrap_mark_coarsen (p4est_wrap_t * pp,
+                         p4est_topidx_t which_tree, p4est_locidx_t which_quad)
+{
+  p4est_t            *p4est = pp->p4est;
+  p4est_tree_t       *tree;
+  p4est_locidx_t      pos;
+  uint8_t             flag;
+
+  P4EST_ASSERT (p4est->first_local_tree <= which_tree);
+  P4EST_ASSERT (which_tree <= p4est->last_local_tree);
+
+  tree = p4est_tree_array_index (p4est->trees, which_tree);
+  P4EST_ASSERT (0 <= which_quad &&
+                which_quad < (p4est_locidx_t) tree->quadrants.elem_count);
+  pos = tree->quadrants_offset + which_quad;
+  P4EST_ASSERT (0 <= pos && pos < p4est->local_num_quadrants);
+
+  flag = pp->flags[pos];
+  if (flag & P4EST_WRAP_REFINE) {
+    pp->flags[pos] &= ~P4EST_WRAP_REFINE;
+    --pp->num_refine_flags;
+  }
+  pp->flags[pos] |= P4EST_WRAP_COARSEN;
+}
+
+int
+p4est_wrap_adapt (p4est_wrap_t * pp)
+{
+  int                 changed;
+#ifdef P4EST_ENABLE_DEBUG
+  p4est_locidx_t      jl, local_num;
+#endif
+  p4est_gloidx_t      global_num;
+  p4est_t            *p4est = pp->p4est;
+
+  P4EST_ASSERT (pp->mesh != NULL);
+  P4EST_ASSERT (pp->ghost != NULL);
+  P4EST_ASSERT (pp->mesh_aux == NULL);
+  P4EST_ASSERT (pp->ghost_aux == NULL);
+  P4EST_ASSERT (pp->match_aux == 0);
+
+  P4EST_ASSERT (pp->temp_flags == NULL);
+  P4EST_ASSERT (pp->num_refine_flags >= 0 &&
+                pp->num_refine_flags <= p4est->local_num_quadrants);
+
+  /* This allocation is optimistic when not all refine requests are honored */
+  pp->temp_flags = P4EST_ALLOC_ZERO (uint8_t, p4est->local_num_quadrants +
+                                     (P4EST_CHILDREN - 1) *
+                                     pp->num_refine_flags);
+
+  /* Execute refinement */
+  pp->inside_counter = pp->num_replaced = 0;
+#ifdef P4EST_ENABLE_DEBUG
+  local_num = p4est->local_num_quadrants;
+#endif
+  global_num = p4est->global_num_quadrants;
+  p4est_refine_ext (p4est, 0, -1, refine_callback, NULL, replace_on_refine);
+  P4EST_ASSERT (pp->inside_counter == local_num);
+  P4EST_ASSERT (p4est->local_num_quadrants - local_num ==
+                pp->num_replaced * (P4EST_CHILDREN - 1));
+  changed = global_num != p4est->global_num_quadrants;
+
+  /* Execute coarsening */
+  pp->inside_counter = pp->num_replaced = 0;
+#ifdef P4EST_ENABLE_DEBUG
+  local_num = p4est->local_num_quadrants;
+#endif
+  global_num = p4est->global_num_quadrants;
+  p4est_coarsen_ext (p4est, 0, 1, coarsen_callback, NULL, NULL);
+  P4EST_ASSERT (pp->inside_counter == local_num);
+  P4EST_ASSERT (local_num - p4est->local_num_quadrants ==
+                pp->num_replaced * (P4EST_CHILDREN - 1));
+  changed = changed || global_num != p4est->global_num_quadrants;
+
+  /* Free temporary flags */
+  P4EST_FREE (pp->temp_flags);
+  pp->temp_flags = NULL;
+
+  /* Only if refinement and/or coarsening happened do we need to balance */
+  if (changed) {
+    P4EST_FREE (pp->flags);
+    p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+    pp->flags = P4EST_ALLOC_ZERO (uint8_t, p4est->local_num_quadrants);
+
+    pp->ghost_aux = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+    pp->mesh_aux = p4est_mesh_new_ext (p4est, pp->ghost_aux, 1, 1,
+                                       P4EST_CONNECT_FULL);
+    pp->match_aux = 1;
+  }
+#ifdef P4EST_ENABLE_DEBUG
+  else {
+    for (jl = 0; jl < p4est->local_num_quadrants; ++jl) {
+      P4EST_ASSERT (pp->flags[jl] == 0);
+    }
+  }
+#endif
+  pp->num_refine_flags = 0;
+
+  return changed;
+}
+
+static int
+partition_weight (p4est_t * p4est, p4est_topidx_t which_tree,
+                  p4est_quadrant_t * quadrant)
+{
+  p4est_wrap_t       *pp = (p4est_wrap_t *) p4est->user_pointer;
+
+  return 1 << ((int) quadrant->level * pp->weight_exponent);
+}
+
+int
+p4est_wrap_partition (p4est_wrap_t * pp, int weight_exponent)
+{
+  int                 changed;
+
+  P4EST_ASSERT (pp->ghost != NULL);
+  P4EST_ASSERT (pp->mesh != NULL);
+  P4EST_ASSERT (pp->ghost_aux != NULL);
+  P4EST_ASSERT (pp->mesh_aux != NULL);
+  P4EST_ASSERT (pp->match_aux == 1);
+
+  p4est_mesh_destroy (pp->mesh);
+  p4est_ghost_destroy (pp->ghost);
+  pp->match_aux = 0;
+
+  /* In the future the flags could be used to pass partition weights */
+  /* We need to lift the restriction on 64 bits for the global weight sum */
+  P4EST_ASSERT (weight_exponent == 0 || weight_exponent == 1);
+  pp->weight_exponent = weight_exponent;
+  changed =
+    p4est_partition_ext (pp->p4est, 1,
+                         weight_exponent ? partition_weight : NULL) > 0;
+
+  if (changed) {
+    P4EST_FREE (pp->flags);
+    pp->flags = P4EST_ALLOC_ZERO (uint8_t, pp->p4est->local_num_quadrants);
+
+    pp->ghost = p4est_ghost_new (pp->p4est, P4EST_CONNECT_FULL);
+    pp->mesh = p4est_mesh_new_ext (pp->p4est, pp->ghost, 1, 1,
+                                   P4EST_CONNECT_FULL);
+  }
+  else {
+    memset (pp->flags, 0, sizeof (uint8_t) * pp->p4est->local_num_quadrants);
+
+    pp->ghost = pp->ghost_aux;
+    pp->mesh = pp->mesh_aux;
+    pp->ghost_aux = NULL;
+    pp->mesh_aux = NULL;
+  }
+
+  return changed;
+}
+
+void
+p4est_wrap_complete (p4est_wrap_t * pp)
+{
+  P4EST_ASSERT (pp->ghost != NULL);
+  P4EST_ASSERT (pp->mesh != NULL);
+  P4EST_ASSERT (pp->ghost_aux != NULL);
+  P4EST_ASSERT (pp->mesh_aux != NULL);
+  P4EST_ASSERT (pp->match_aux == 0);
+
+  p4est_mesh_destroy (pp->mesh_aux);
+  p4est_ghost_destroy (pp->ghost_aux);
+  pp->ghost_aux = NULL;
+  pp->mesh_aux = NULL;
+}
+
+static p4est_wrap_leaf_t *
+p4est_wrap_leaf_info (p4est_wrap_leaf_t * leaf)
+{
+#if 0
+#ifdef P4EST_ENABLE_DEBUG
+  int                 nface;
+  p4est_mesh_t       *mesh = p4est_wrap_get_mesh (leaf->pp);
+#endif
+#endif
+  p4est_quadrant_t    corner;
+
+  leaf->total_quad = leaf->tree->quadrants_offset + leaf->which_quad;
+  leaf->quad = p4est_quadrant_array_index (&leaf->tree->quadrants,
+                                           leaf->which_quad);
+
+  leaf->level = (int) leaf->quad->level;
+  p4est_qcoord_to_vertex (leaf->pp->conn, leaf->which_tree,
+                          leaf->quad->x, leaf->quad->y,
+#ifdef P4_TO_P8
+                          leaf->quad->z,
+#endif
+                          leaf->lowerleft);
+  p4est_quadrant_corner_node (leaf->quad, P4EST_CHILDREN - 1, &corner);
+  p4est_qcoord_to_vertex (leaf->pp->conn, leaf->which_tree,
+                          corner.x, corner.y,
+#ifdef P4_TO_P8
+                          corner.z,
+#endif
+                          leaf->upperright);
+
+#if 0
+#ifdef P4EST_ENABLE_DEBUG
+  printf ("C: Leaf level %d tree %d tree_leaf %d local_leaf %d\n",
+          leaf->level, leaf->which_tree, leaf->which_quad, leaf->total_quad);
+  for (nface = 0; nface < P4EST_FACES; ++nface) {
+    printf ("C: Leaf face %d neighbor leaf %d\n", nface,
+            mesh->quad_to_quad[P4EST_FACES * leaf->total_quad + nface]);
+  }
+#endif
+#endif
+
+  return leaf;
+}
+
+p4est_wrap_leaf_t  *
+p4est_wrap_leaf_first (p4est_wrap_t * pp)
+{
+  p4est_wrap_leaf_t  *leaf;
+  p4est_t            *p4est = pp->p4est;
+
+  if (p4est->local_num_quadrants == 0) {
+    return NULL;
+  }
+
+  leaf = P4EST_ALLOC (p4est_wrap_leaf_t, 1);
+  leaf->pp = pp;
+  leaf->which_tree = p4est->first_local_tree;
+  leaf->tree = p4est_tree_array_index (p4est->trees, leaf->which_tree);
+  P4EST_ASSERT (leaf->tree->quadrants.elem_size > 0);
+  leaf->which_quad = 0;
+
+  return p4est_wrap_leaf_info (leaf);
+}
+
+p4est_wrap_leaf_t  *
+p4est_wrap_leaf_next (p4est_wrap_leaf_t * leaf)
+{
+  p4est_t            *p4est = leaf->pp->p4est;
+
+  P4EST_ASSERT (leaf != NULL);
+
+  if ((size_t) leaf->which_quad + 1 == leaf->tree->quadrants.elem_count) {
+    ++leaf->which_tree;
+    if (leaf->which_tree > p4est->last_local_tree) {
+      P4EST_FREE (leaf);
+      return NULL;
+    }
+    leaf->tree = p4est_tree_array_index (p4est->trees, leaf->which_tree);
+    P4EST_ASSERT (leaf->tree->quadrants.elem_size > 0);
+    leaf->which_quad = 0;
+  }
+  else {
+    ++leaf->which_quad;
+  }
+
+  return p4est_wrap_leaf_info (leaf);
+}
diff --git a/src/p4est_wrap.h b/src/p4est_wrap.h
new file mode 100644
index 0000000..1801bdf
--- /dev/null
+++ b/src/p4est_wrap.h
@@ -0,0 +1,197 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_WRAP_H
+#define P4EST_WRAP_H
+
+#include <p4est_mesh.h>
+
+SC_EXTERN_C_BEGIN;
+
+/*** COMPLETE INTERNAL STATE OF P4EST ***/
+
+typedef enum p4est_wrap_flags
+{
+  P4EST_WRAP_NONE = 0,
+  P4EST_WRAP_REFINE = 0x01,
+  P4EST_WRAP_COARSEN = 0x02
+}
+p4est_wrap_flags_t;
+
+typedef struct p4est_wrap
+{
+  /* this member is never used or changed by p4est_wrap */
+  void               *user_pointer;     /**< Convenience member for users */
+
+  /* these members are considered public and read-only */
+  int                 p4est_dim;
+  int                 p4est_half;
+  int                 p4est_faces;
+  int                 p4est_children;
+  p4est_connectivity_t *conn;
+  p4est_t            *p4est;    /**< p4est->user_pointer is used internally */
+
+  /* anything below here is considered private und should not be touched */
+  int                 weight_exponent;
+  uint8_t            *flags, *temp_flags;
+  p4est_locidx_t      num_refine_flags, inside_counter, num_replaced;
+
+  /* for ghost and mesh use p4est_wrap_get_ghost, _mesh declared below */
+  p4est_ghost_t      *ghost;
+  p4est_mesh_t       *mesh;
+  p4est_ghost_t      *ghost_aux;
+  p4est_mesh_t       *mesh_aux;
+  int                 match_aux;
+}
+p4est_wrap_t;
+
+/** Create a p4est wrapper from a given connectivity structure.
+ * \param [in] mpicomm        We expect sc_MPI_Init to be called already.
+ * \param [in] conn           Connectivity structure.  Wrap takes ownership.
+ * \param [in] initial_level  Initial level of uniform refinement.
+ * \return                    A fully initialized p4est_wrap structure.
+ */
+p4est_wrap_t       *p4est_wrap_new_conn (sc_MPI_Comm mpicomm,
+                                         p4est_connectivity_t * conn,
+                                         int initial_level);
+
+/** Create p4est and auxiliary data structures.
+ * Expects sc_MPI_Init to be called beforehand.
+ */
+p4est_wrap_t       *p4est_wrap_new_unitsquare (sc_MPI_Comm mpicomm,
+                                               int initial_level);
+p4est_wrap_t       *p4est_wrap_new_periodic (sc_MPI_Comm mpicomm,
+                                             int initial_level);
+p4est_wrap_t       *p4est_wrap_new_rotwrap (sc_MPI_Comm mpicomm,
+                                            int initial_level);
+p4est_wrap_t       *p4est_wrap_new_corner (sc_MPI_Comm mpicomm,
+                                           int initial_level);
+p4est_wrap_t       *p4est_wrap_new_pillow (sc_MPI_Comm mpicomm,
+                                           int initial_level);
+p4est_wrap_t       *p4est_wrap_new_moebius (sc_MPI_Comm mpicomm,
+                                            int initial_level);
+p4est_wrap_t       *p4est_wrap_new_cubed (sc_MPI_Comm mpicomm,
+                                          int initial_level);
+p4est_wrap_t       *p4est_wrap_new_disk (sc_MPI_Comm mpicomm,
+                                         int initial_level);
+
+/** Passes sc_MPI_COMM_WORLD to p4est_wrap_new_unitsquare. */
+p4est_wrap_t       *p4est_wrap_new_world (int initial_level);
+void                p4est_wrap_destroy (p4est_wrap_t * pp);
+
+/** Return the appropriate ghost layer.
+ * This function is necessary since two versions may exist simultaneously
+ * after refinement and before partition/complete.
+ * */
+p4est_ghost_t      *p4est_wrap_get_ghost (p4est_wrap_t * pp);
+
+/** Return the appropriate mesh structure.
+ * This function is necessary since two versions may exist simultaneously
+ * after refinement and before partition/complete.
+ * */
+p4est_mesh_t       *p4est_wrap_get_mesh (p4est_wrap_t * pp);
+
+/** Mark a local element for refinement.
+ * This will cancel any coarsening mark set previously for this element.
+ * \param [in,out] wrap The p4est wrapper to work with.
+ * \param [in] which_tree The number of the tree this element lives in.
+ * \param [in] which_quad The number of this element relative to its tree.
+ */
+void                p4est_wrap_mark_refine (p4est_wrap_t * pp,
+                                            p4est_topidx_t which_tree,
+                                            p4est_locidx_t which_quad);
+
+/** Mark a local element for coarsening.
+ * This will cancel any refinement mark set previously for this element.
+ * \param [in,out] wrap The p4est wrapper to work with.
+ * \param [in] which_tree The number of the tree this element lives in.
+ * \param [in] which_quad The number of this element relative to its tree.
+ */
+void                p4est_wrap_mark_coarsen (p4est_wrap_t * pp,
+                                             p4est_topidx_t which_tree,
+                                             p4est_locidx_t which_quad);
+
+/** Call p4est_refine, coarsen, and balance to update pp->p4est.
+ * Checks pp->flags as per-quadrant input against p4est_wrap_flags_t.
+ * The pp->flags array is updated along with p4est and reset to zeros.
+ * Creates ghost_aux and mesh_aux to represent the intermediate mesh.
+ * \return          boolean whether p4est has changed.
+ *                  If true, partition must be called.
+ *                  If false, partition must not be called, and
+ *                  complete must not be called either.
+ */
+int                 p4est_wrap_adapt (p4est_wrap_t * pp);
+
+/** Call p4est_partition for equal leaf distribution.
+ * Frees the old ghost and mesh first and updates pp->flags along with p4est.
+ * The pp->flags array is reset to zeros.
+ * Creates ghost and mesh to represent the new mesh.
+ * \param [in] weight_exponent      Integer weight assigned to each leaf
+ *                  according to 2 ** (level * exponent).  Passing 0 assigns
+ *                  equal weight to all leaves.  Passing 1 increases the
+ *                  leaf weight by a factor of two for each level increase.
+ *                  CURRENTLY ONLY 0 AND 1 ARE LEGAL VALUES.
+ * \return          boolean whether p4est has changed.
+ *                  If true, complete must be called.
+ *                  If false, complete must not be called.
+ */
+int                 p4est_wrap_partition (p4est_wrap_t * pp,
+                                          int weight_exponent);
+
+/** Free memory for the intermediate mesh.
+ * Sets mesh_aux and ghost_aux to NULL.
+ * This function must be used if both refinement and partition effect changes.
+ * After this call, we are ready for another mark-refine-partition cycle.
+ */
+void                p4est_wrap_complete (p4est_wrap_t * pp);
+
+/*** ITERATOR OVER THE FOREST LEAVES ***/
+
+typedef struct p4est_wrap_leaf
+{
+  p4est_wrap_t       *pp;
+  int                 level;
+  p4est_topidx_t      which_tree;
+  p4est_locidx_t      which_quad;
+  p4est_locidx_t      total_quad;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *quad;
+  double              lowerleft[3];
+  double              upperright[3];
+}
+p4est_wrap_leaf_t;
+
+/* Create an iterator over the leaves in the forest.
+ * Returns a newly allocated state containing the first leaf,
+ * or NULL if the local partition of the tree is empty.
+ */
+p4est_wrap_leaf_t  *p4est_wrap_leaf_first (p4est_wrap_t * pp);
+
+/* Move the forest leaf iterator forward.
+ * Returns the state that was input with information for the next leaf,
+ * or NULL and deallocates the input if called with the last leaf.
+ */
+p4est_wrap_leaf_t  *p4est_wrap_leaf_next (p4est_wrap_leaf_t * leaf);
+
+SC_EXTERN_C_END;
+
+#endif /* !P4EST_WRAP_H */
diff --git a/src/p8est.c b/src/p8est.c
new file mode 100644
index 0000000..c90fb08
--- /dev/null
+++ b/src/p8est.c
@@ -0,0 +1,28 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est.c"
+
+static int          p8est_uninitialized_key;
+void               *P8EST_DATA_UNINITIALIZED = &p8est_uninitialized_key;
diff --git a/src/p8est.h b/src/p8est.h
new file mode 100644
index 0000000..ee30860
--- /dev/null
+++ b/src/p8est.h
@@ -0,0 +1,473 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est.h
+ *
+ * The top-level 3D p8est interface.
+ *
+ * \ingroup p8est
+ */
+
+/** \defgroup p8est p8est
+ *
+ * The 3D version of the p4est library.
+ */
+
+#ifndef P8EST_H
+#define P8EST_H
+
+/* p8est_connectivity.h includes p4est_base.h sc_containers.h */
+#include <p8est_connectivity.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** The finest level of the octree for representing nodes */
+#define P8EST_MAXLEVEL 19
+
+/** The finest level of the octree for representing octants */
+#define P8EST_QMAXLEVEL 18
+
+/** The length of a side of the root quadrant */
+#define P8EST_ROOT_LEN ((p4est_qcoord_t) 1 << P8EST_MAXLEVEL)
+
+/** The length of a quadrant of level l */
+#define P8EST_QUADRANT_LEN(l) ((p4est_qcoord_t) 1 << (P8EST_MAXLEVEL - (l)))
+
+/** The offset of the highest (farthest from the origin) quadrant at level l
+ */
+#define P8EST_LAST_OFFSET(l) (P8EST_ROOT_LEN - P8EST_QUADRANT_LEN (l))
+
+/** The 3D quadrant (i.e. octant) datatype */
+typedef struct p8est_quadrant
+{
+  /*@{*/
+  p4est_qcoord_t      x, y, z;  /**< coordinates */
+  /*@}*/
+  int8_t              level,    /**< level of refinement */
+                      pad8;     /**< padding */
+  int16_t             pad16;    /**< padding */
+  union p8est_quadrant_data
+  {
+    void               *user_data;      /**< never changed by p4est */
+    long                user_long;      /**< never changed by p4est */
+    int                 user_int;       /**< never changed by p4est */
+    p4est_topidx_t      which_tree;     /**< the tree containing the quadrant
+                                             (used in auxiliary octants such
+                                             as the ghost octants in
+                                             p4est_ghost_t) */
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      int                 owner_rank;
+    }
+    piggy1; /**< of ghost octants, store the tree and owner rank */
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_topidx_t      from_tree;
+    }
+    piggy2; /**< of transformed octants, store the original tree and the
+                 target tree */
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      local_num;
+    }
+    piggy3; /**< of ghost octants, store the tree and index in the owner's
+                 numbering */
+  }
+  p; /**< a union of additional data attached to a quadrant */
+}
+p8est_quadrant_t;
+
+/** The p8est tree datatype */
+typedef struct p8est_tree
+{
+  sc_array_t          quadrants;             /**< locally stored quadrants */
+  p8est_quadrant_t    first_desc,            /**< first local descendant */
+                      last_desc;             /**< last local descendant */
+  p4est_locidx_t      quadrants_offset;      /**< cumulative sum over earlier
+                                                  trees on this processor
+                                                  (locals only) */
+  p4est_locidx_t      quadrants_per_level[P8EST_MAXLEVEL + 1];
+                                             /**< locals only */
+  int8_t              maxlevel;              /**< highest local quadrant level */
+}
+p8est_tree_t;
+
+/** Data pertaining to selecting, inspecting, and profiling algorithms.
+ * A pointer to this structure is hooked into the p8est main structure.
+ * Declared in p8est_extended.h.  Used to profile important algorithms.
+ */
+typedef struct p8est_inspect p8est_inspect_t;
+
+/** The p8est forest datatype */
+typedef struct p8est
+{
+  sc_MPI_Comm         mpicomm;          /**< MPI communicator */
+  int                 mpisize,          /**< number of MPI processes */
+                      mpirank;          /**< this process's MPI rank */
+  size_t              data_size;        /**< size of per-quadrant p.user_data
+                     (see p8est_quadrant_t::p8est_quadrant_data::user_data) */
+  void               *user_pointer;     /**< convenience pointer for users,
+                                             never touched by p4est */
+
+  p4est_topidx_t      first_local_tree; /**< 0-based index of first local
+                                             tree, must be -1 for an empty
+                                             processor */
+  p4est_topidx_t      last_local_tree;  /**< 0-based index of last local
+                                             tree, must be -2 for an empty
+                                             processor */
+  p4est_locidx_t      local_num_quadrants;   /**< number of quadrants on all
+                                                  trees on this processor */
+  p4est_gloidx_t      global_num_quadrants;  /**< number of quadrants on all
+                                                  trees on all processors */
+  p4est_gloidx_t     *global_first_quadrant; /**< first global quadrant index
+                                                  for each process and 1
+                                                  beyond */
+  p8est_quadrant_t   *global_first_position; /**< first smallest possible quad
+                                                  for each process and 1
+                                                  beyond */
+  p8est_connectivity_t *connectivity; /**< connectivity structure, not owned */
+  sc_array_t         *trees;          /**< array of all trees */
+
+  sc_mempool_t       *user_data_pool; /**< memory allocator for user data */
+                                      /*   WARNING: This is NULL if data size
+                                                    equals zero. */
+  sc_mempool_t       *quadrant_pool;  /**< memory allocator for temporary
+                                           quadrants */
+  p8est_inspect_t    *inspect;        /**< algorithmic switches */
+}
+p8est_t;
+
+/** Calculate memory usage of a forest structure.
+ * The connectivity structure is not counted since it is not owned;
+ * use p8est_connectivity_memory_usage (p8est->connectivity).
+ * \param [in] p8est    Forest structure.
+ * \return              Memory used in bytes.
+ */
+size_t              p8est_memory_used (p8est_t * p8est);
+
+/** Callback function prototype to initialize the quadrant's user data.
+ * \param [in] p8est         the forest
+ * \param [in] which_tree    the tree containing \a quadrant
+ * \param [in,out] quadrant  the quadrant to be initialized: if data_size > 0,
+ *                           the data to be initialized is at
+ *                           \a quadrant->p.user_data; otherwise, the
+ *                           non-pointer user data (such as
+ *                           \a quadrant->p.user_int) can be initialized
+ */
+typedef void        (*p8est_init_t) (p8est_t * p8est,
+                                     p4est_topidx_t which_tree,
+                                     p8est_quadrant_t * quadrant);
+
+/** Callback function prototype to decide for refinement.
+ * \param [in] p8est       the forest
+ * \param [in] which_tree  the tree containing \a quadrant
+ * \param [in] quadrant    the quadrant that may be refined
+ * \return nonzero if the quadrant shall be refined.
+ */
+typedef int         (*p8est_refine_t) (p8est_t * p8est,
+                                       p4est_topidx_t which_tree,
+                                       p8est_quadrant_t * quadrant);
+
+/** Callback function prototype to decide for coarsening.
+ * \param [in] p8est       the forest
+ * \param [in] which_tree  the tree containing \a quadrant
+ * \param [in] quadrants   Pointers to 8 siblings in Morton ordering.
+ * \return nonzero if the quadrants shall be replaced with their parent.
+ */
+typedef int         (*p8est_coarsen_t) (p8est_t * p8est,
+                                        p4est_topidx_t which_tree,
+                                        p8est_quadrant_t * quadrants[]);
+
+/** Callback function prototype to calculate weights for partitioning.
+ * \param [in] p8est       the forest
+ * \param [in] which_tree  the tree containing \a quadrant
+ * \return a 32bit integer >= 0 as the quadrant weight.
+ * \note    Global sum of weights must fit into a 64bit integer.
+ */
+typedef int         (*p8est_weight_t) (p8est_t * p8est,
+                                       p4est_topidx_t which_tree,
+                                       p8est_quadrant_t * quadrant);
+
+extern void        *P8EST_DATA_UNINITIALIZED;
+
+/** set statically allocated quadrant to defined values */
+#define P8EST_QUADRANT_INIT(q) \
+  ((void) memset ((q), -1, sizeof (p8est_quadrant_t)))
+
+/** Transform a quadrant coordinate into the space spanned by tree vertices.
+ * \param [in] connectivity     Connectivity must provide the vertices.
+ * \param [in] treeid           Identify the tree that contains x, y, z.
+ * \param [in] x, y, z          Quadrant coordinates relative to treeid.
+ * \param [out] vxyz            Transformed coordinates in vertex space.
+ */
+void                p8est_qcoord_to_vertex (p8est_connectivity_t *
+                                            connectivity,
+                                            p4est_topidx_t treeid,
+                                            p4est_qcoord_t x,
+                                            p4est_qcoord_t y,
+                                            p4est_qcoord_t z, double vxyz[3]);
+
+/** Create a new forest.
+ * The new forest consists of equi-partitioned root quadrants.
+ * When there are more processors than trees, some processors are empty.
+ *
+ * \param [in] mpicomm       A valid MPI communicator.
+ * \param [in] connectivity  This is the connectivity information that
+ *                           the forest is built with.  Note the p8est
+ *                           does not take ownership of the memory.
+ * \param [in] data_size     This is the size of data for each quadrant which
+ *                           can be zero.  Then user_data_pool is set to NULL.
+ * \param [in] init_fn       Callback function to initialize the user_data
+ *                           which is already allocated automatically.
+ * \param [in] user_pointer  Assign to the user_pointer member of the p8est
+ *                           before init_fn is called the first time.
+ *
+ * \return This returns a valid forest.
+ *
+ * \note The connectivity structure must not be destroyed
+ *       during the lifetime of this forest.
+ */
+p8est_t            *p8est_new (sc_MPI_Comm mpicomm,
+                               p8est_connectivity_t * connectivity,
+                               size_t data_size,
+                               p8est_init_t init_fn, void *user_pointer);
+
+/** Destroy a p8est.
+ *
+ * \note The connectivity structure is not destroyed with the p8est.
+ */
+void                p8est_destroy (p8est_t * p8est);
+
+/** Make a deep copy of a p8est.
+ * The connectivity is not duplicated.
+ * Copying of quadrant user data is optional.
+ * If old and new data sizes are 0, the user_data field is copied regardless.
+ * The inspect member of the copy is set to NULL.
+ *
+ * \param [in]  copy_data  If true, data are copied.
+ *                         If false, data_size is set to 0.
+ * \return  Returns a valid p8est that does not depend on the input.
+ */
+p8est_t            *p8est_copy (p8est_t * input, int copy_data);
+
+/** Reset user pointer and element data.
+ * When the data size is changed the quadrant data is freed and allocated.
+ * The initialization callback is invoked on each quadrant.
+ * Old user_data content is disregarded.
+ *
+ * \param [in] data_size     This is the size of data for each quadrant which
+ *                           can be zero.  Then user_data_pool is set to NULL.
+ * \param [in] init_fn       Callback function to initialize the user_data
+ *                           which is already allocated automatically.
+ *                           May be NULL.
+ * \param [in] user_pointer  Assign to the user_pointer member of the p8est
+ *                           before init_fn is called the first time.
+ */
+void                p8est_reset_data (p8est_t * p8est, size_t data_size,
+                                      p8est_init_t init_fn,
+                                      void *user_pointer);
+
+/** Refine a forest.
+ * \param [in,out] p8est The forest is changed in place.
+ * \param [in] refine_recursive Boolean to decide on recursive refinement.
+ * \param [in] refine_fn Callback function that must return true if a quadrant
+ *                       shall be refined.  If refine_recursive is true,
+ *                       refine_fn is called for every existing and newly
+ *                       created quadrant.  Otherwise, it is called for every
+ *                       existing quadrant.  It is possible that a refinement
+ *                       request made by the callback is ignored.  To catch
+ *                       this case, you can examine whether init_fn gets
+ *                       called, or use p8est_refine_ext in p8est_extended.h
+ *                       and examine whether replace_fn gets called.
+ * \param [in] init_fn   Callback function to initialize the user_data of newly
+ *                       created quadrants, which is already allocated.  This
+ *                       function pointer may be NULL.
+ */
+void                p8est_refine (p8est_t * p8est,
+                                  int refine_recursive,
+                                  p8est_refine_t refine_fn,
+                                  p8est_init_t init_fn);
+
+/** Coarsen a forest.
+ * \param [in,out] p8est  The forest is changed in place.
+ * \param [in] coarsen_recursive Boolean to decide on recursive coarsening.
+ * \param [in] coarsen_fn Callback function that returns true if a
+ *                        family of quadrants shall be coarsened
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ */
+void                p8est_coarsen (p8est_t * p8est,
+                                   int coarsen_recursive,
+                                   p8est_coarsen_t coarsen_fn,
+                                   p8est_init_t init_fn);
+
+/** 2:1 balance the size differences of neighboring elements in a forest.
+ * \param [in,out] p8est  The p8est to be worked on.
+ * \param [in] btype      Balance type (face, edge, or corner/full).  Examples:
+ *                        Finite volume or discontinuous Galerkin methods only
+ *                        require face balance.  Continuous finite element
+ *                        methods usually require edge balance.  Corner balance
+ *                        is almost never required mathematically; it just
+ *                        produces a smoother mesh grading.
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ */
+void                p8est_balance (p8est_t * p8est,
+                                   p8est_connect_type_t btype,
+                                   p8est_init_t init_fn);
+
+/** Equally partition the forest.
+ * The partition can be by element count or by a user-defined weight.
+ *
+ * The forest will be partitioned between processors such that they
+ * have an approximately equal number of quadrants (or sum of weights).
+ *
+ * \param [in,out] p8est      The forest that will be partitioned.
+ * \param [in]     allow_for_coarsening Slightly modify partition such that
+ *                            quadrant families are not split between ranks.
+ * \param [in]     weight_fn  A weighting function or NULL
+ *                            for uniform partitioning.
+ */
+void                p8est_partition (p8est_t * p8est,
+                                     int allow_for_coarsening,
+                                     p8est_weight_t weight_fn);
+
+/** Compute the checksum for a forest.
+ * Based on quadrant arrays only. It is independent of partition and mpisize.
+ * \return  Returns the checksum on processor 0 only. 0 on other processors.
+ */
+unsigned            p8est_checksum (p8est_t * p8est);
+
+/** Save the complete connectivity/p8est data to disk.
+ *
+ * This is a collective operation that all MPI processes need to call.  All
+ * processes write into the same file, so the filename given needs to be
+ * identical over all parallel invocations.
+ *
+ * By default, we write the current processor count and partition into the file
+ * header.  This makes the file depend on mpisize.  For changing this see
+ * p8est_save_ext() in p8est_extended.h.
+ *
+ * \param [in] filename    Name of the file to write.
+ * \param [in] p8est       Valid forest structure.
+ * \param [in] save_data   If true, the element data is saved.
+ *                         Otherwise, a data size of 0 is saved.
+ * \note            Aborts on file errors.
+ * \note            If p4est is not configured to use MPI-IO, some processes
+ *                  return from this function before the file is complete, in
+ *                  which case immediate read-access to the file may require a
+ *                  call to sc_MPI_Barrier.
+ */
+void                p8est_save (const char *filename, p8est_t * p8est,
+                                int save_data);
+
+/** Load the complete connectivity/p8est structure from disk.
+ *
+ * This is a collective operation that all MPI processes need to call.  All
+ * processes read from the same file, so the filename given needs to be
+ * identical over all parallel invocations.
+ *
+ * By default, a file can only be loaded with the same number of processors
+ * that it was stored with.  The defaults can be changed with p8est_load_ext()
+ * in p8est_extended.h.
+ *
+ * \param [in] filename         Name of the file to read.
+ * \param [in] mpicomm          A valid MPI communicator.
+ * \param [in] data_size        Size of data for each quadrant which can be
+ *                              zero.  Then user_data_pool is set to NULL.
+ *                              If data_size is zero, load_data is ignored.
+ * \param [in] load_data        If true, the element data is loaded.  This is
+ *                              only permitted if the saved data size matches.
+ *                              If false, the stored data size is ignored.
+ * \param [in] user_pointer     Assign to the user_pointer member of the p8est
+ *                              before init_fn is called the first time.
+ * \param [out] connectivity    Connectivity must be destroyed separately.
+ * \return          Returns a valid forest structure. A pointer to a valid
+ *                  connectivity structure is returned through the last
+ *                  argument.
+ * \note            Aborts on file errors or invalid file contents.
+ */
+p8est_t            *p8est_load (const char *filename, sc_MPI_Comm mpicomm,
+                                size_t data_size, int load_data,
+                                void *user_pointer,
+                                p8est_connectivity_t ** connectivity);
+
+/** Return a pointer to an array element indexed by a p4est_topidx_t.
+ * \param [in] index needs to be in [0]..[elem_count-1].
+ */
+/*@unused@*/
+static inline p8est_tree_t *
+p8est_tree_array_index (sc_array_t * array, p4est_topidx_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_tree_t));
+  P4EST_ASSERT (it >= 0 && (size_t) it < array->elem_count);
+
+  return (p8est_tree_t *) (array->array +
+                           sizeof (p8est_tree_t) * (size_t) it);
+}
+
+/** Return a pointer to a quadrant array element indexed by a size_t. */
+/*@unused@*/
+static inline p8est_quadrant_t *
+p8est_quadrant_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_quadrant_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p8est_quadrant_t *) (array->array + sizeof (p8est_quadrant_t) * it);
+}
+
+/** Call sc_array_push for a quadrant array. */
+/*@unused@*/
+static inline p8est_quadrant_t *
+p8est_quadrant_array_push (sc_array_t * array)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_quadrant_t));
+
+  return (p8est_quadrant_t *) sc_array_push (array);
+}
+
+/** Call sc_mempool_alloc for a mempool creating quadrants. */
+/*@unused@*/
+static inline p8est_quadrant_t *
+p8est_quadrant_mempool_alloc (sc_mempool_t * mempool)
+{
+  P4EST_ASSERT (mempool->elem_size == sizeof (p8est_quadrant_t));
+
+  return (p8est_quadrant_t *) sc_mempool_alloc (mempool);
+}
+
+/** Call sc_list pop for a quadrant array. */
+/*@unused@*/
+static inline p8est_quadrant_t *
+p8est_quadrant_list_pop (sc_list_t * list)
+{
+  return (p8est_quadrant_t *) sc_list_pop (list);
+}
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_H */
diff --git a/src/p8est_algorithms.c b/src/p8est_algorithms.c
new file mode 100644
index 0000000..5d38a53
--- /dev/null
+++ b/src/p8est_algorithms.c
@@ -0,0 +1,73 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include <p8est_connectivity.h>
+
+/* *INDENT-OFF* */
+
+/** Store the number of quadrants to add for complete and balance stages. */
+static const int    p4est_balance_count[P4EST_DIM + 1] =
+{ 9, 12, 15, 16 };
+
+/** Store coordinates of quadrants to add for balancing. */
+static const p4est_qcoord_t p4est_balance_coord[26][P4EST_DIM] =
+{ /* faces */
+  { -1,  1,  1 },
+  {  2,  0,  0 },
+  {  1, -1,  1 },
+  {  0,  2,  0 },
+  {  1,  1, -1 },
+  {  0,  0,  2 },
+  /* edges */
+  {  1, -1, -1 },
+  {  1,  2, -1 },
+  {  0, -1,  2 },
+  {  0,  2,  2 },
+  { -1,  1, -1 },
+  {  2,  1, -1 },
+  { -1,  0,  2 },
+  {  2,  0,  2 },
+  { -1, -1,  1 },
+  {  2, -1,  1 },
+  { -1,  2,  0 },
+  {  2,  2,  0 },
+  /* corners */
+  { -1, -1, -1 },
+  {  2, -1, -1 },
+  { -1,  2, -1 },
+  {  2,  2, -1 },
+  { -1, -1,  2 },
+  {  2, -1,  2 },
+  { -1,  2,  2 },
+  {  2,  2,  2 }};
+
+/** Offset for edges into p4est_balance_coord */
+static const int    pbeo = P4EST_FACES;
+
+/** Offset for corners into p4est_balance_coord */
+static const int    pbco = P4EST_FACES + P8EST_EDGES;
+
+/* *INDENT-ON* */
+
+#include "p4est_algorithms.c"
diff --git a/src/p8est_algorithms.h b/src/p8est_algorithms.h
new file mode 100644
index 0000000..80c50d1
--- /dev/null
+++ b/src/p8est_algorithms.h
@@ -0,0 +1,304 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P8EST_ALGORITHMS_H
+#define P8EST_ALGORITHMS_H
+
+#include <p8est.h>
+#include <p8est_extended.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Alloc and initialize the user data of a valid quadrant.
+ * \param [in]  which_tree 0-based index of this quadrant's tree.
+ * \param [in,out]  quad       The quadrant to be initialized.
+ * \param [in]  init_fn    User-supplied callback function to init data.
+ */
+void                p8est_quadrant_init_data (p8est_t * p8est,
+                                              p4est_topidx_t which_tree,
+                                              p8est_quadrant_t * quad,
+                                              p8est_init_t init_fn);
+
+/** Free the user data of a valid quadrant.
+ * \param [in,out]  quad The quadrant whose data shall be freed.
+ */
+void                p8est_quadrant_free_data (p8est_t * p8est,
+                                              p8est_quadrant_t * quad);
+
+/** Computes a machine-independent checksum of a list of quadrants.
+ * \param [in] quadrants       Array of quadrants.
+ * \param [in,out] checkarray  Temporary array of elem_size 4.
+ *                             Will be resized to quadrants->elem_count * 3.
+ *                             If it is NULL, will be allocated internally.
+ * \param [in] first_quadrant  Index of the quadrant to start with.
+ *                             Can be between 0 and elem_count (inclusive).
+ */
+unsigned            p8est_quadrant_checksum (sc_array_t * quadrants,
+                                             sc_array_t * checkarray,
+                                             size_t first_quadrant);
+
+/** Test if a tree is sorted in Morton ordering.
+ * \return Returns true if sorted, false otherwise.
+ * \note Duplicate quadrants are not allowed.
+ */
+int                 p8est_tree_is_sorted (p8est_tree_t * tree);
+
+/** Test if a tree is sorted in Morton ordering and linear.
+ * \return Returns true if linear, false otherwise.
+ * \note Linear means that the tree has no overlaps.
+ */
+int                 p8est_tree_is_linear (p8est_tree_t * tree);
+
+/** Test if a tree is sorted in Morton ordering and complete.
+ * \return Returns true if complete, false otherwise.
+ * \note Complete means that the tree has no holes and no overlaps.
+ */
+int                 p8est_tree_is_complete (p8est_tree_t * tree);
+
+/** Check if a tree is sorted/linear except across edges or corners.
+ * \param [in]  check_linearity  Boolean for additional check for linearity.
+ * \return Returns true if almost sorted/linear, false otherwise.
+ */
+int                 p8est_tree_is_almost_sorted (p8est_tree_t * tree,
+                                                 int check_linearity);
+
+/** Print the quadrants in a tree.
+ * Prints one line per quadrant with x, y, level and a string.
+ * The string denotes the relation to the previous quadrant and can be:
+ *   Fn  for the first quadrant in the tree with child id n
+ *   I   for identical quadrants
+ *   R   for a quadrant that with smaller Morton index
+ *   Cn  for child id n
+ *   Sn  for sibling with child id n
+ *   D   for a descendant
+ *   Nn   for a next quadrant in the tree with no holes in between and child id n
+ *   qn  for a general quadrant whose child id is n
+ * \param [in] tree        Any (possibly incomplete, unsorted) tree to be printed.
+ */
+void                p8est_tree_print (int log_priority, p8est_tree_t * tree);
+
+/** Locally check forest/connectivity structures for equality.
+ * \param [in] p8est1    The first forest to be compared.
+ * \param [in] p8est2    The second forest to be compared.
+ * \param [in] compare_data     Also check if quadrant data are identical.
+ * \return          Returns true if forests and their connectivities are equal.
+ */
+int                 p8est_is_equal (p8est_t * p8est1, p8est_t * p8est2,
+                                    int compare_data);
+
+/** Check a forest for validity and allreduce the result.
+ * Some properties of a valid forest are:
+ *    the quadrant counters are consistent
+ *    all trees are complete
+ *    all non-local trees are empty
+ * \param [in] p8est    The forest to be tested.
+ * \return              Returns true if valid, false otherwise.
+ */
+int                 p8est_is_valid (p8est_t * p8est);
+
+/** Compute the overlap of a number of insulation layers with a tree.
+ * Every quadrant out of the insulation layer of the quadrants in \a in
+ * except the quadrant itself is checked for overlap of quadrants
+ * from all trees that are smaller by at least two levels and thus
+ * can cause a split. Those elements that cause a split (as determined by the
+ * p8est_balance_*_test routines) create quadrants in \a out that will
+ * reproduce those splits when \a in is balanced.
+ * Note: Use this version if you are using less than full balance.
+ *
+ * \param [in] p4est    The p8est to work on.
+ * \param [in] in       A piggy-sorted linear list of quadrants.
+ *                      The piggy2->from_tree member must be set.
+ * \param [in,out] out  A piggy-sorted subset of tree->quadrants.
+ * \param [in] balance  The type of balance condition that should be enforced.
+ * \param [in] borders  Array of arrays of tree border elements: if not NULL,
+ *                      this will be used to fill \a out.
+ * \param [in] inseeds  The seeds that \a in generates locally.
+ */
+void                p8est_tree_compute_overlap (p8est_t * p8est,
+                                                sc_array_t * in,
+                                                sc_array_t * out,
+                                                p8est_connect_type_t
+                                                balance,
+                                                sc_array_t * borders,
+                                                sc_array_t * inseeds);
+
+/** Gets the reduced representation of the overlap that results from using
+ * p8est_tree_compute_overlap_new
+ * \param [in,out] out  A piggy-sorted subset of tree->quadrants.
+  */
+void                p8est_tree_uniqify_overlap (sc_array_t * out);
+
+/** Removes quadrants that are outside the owned tree boundaries from a tree.
+ * \param [in,out] p8est    The p8est to work on.
+ * \param [in] which_tree   Index to a sorted owned tree in the p8est.
+ * \return                  Returns the number of removed quadrants.
+ */
+size_t              p8est_tree_remove_nonowned (p8est_t * p8est,
+                                                p4est_topidx_t which_tree);
+
+/** Constructs a minimal linear octree between two octants.
+ *
+ * This is alogorithm 2 from H. Sundar, R.S. Sampath and G. Biros
+ * with the additional improvements that we do not require sorting
+ * and the runtime is O(N).
+ *
+ * \pre \a q1 < \a q2 in the Morton ordering.
+ *
+ * \param [in]  p8est      Used for the memory pools and quadrant init.
+ * \param [in]  q1         First input quadrant.  Data init'ed if included.
+ * \param [in]  include_q1 Flag to specify whether q1 is included.
+ * \param [in]  q2         Second input quadrant.  Data init'ed if included.
+ * \param [in]  include_q2 Flag to specify whether q2 is included.
+ * \param [out] tree       Initialized tree with zero elements.
+ * \param [in]  which_tree The 0-based index of \a tree which is needed for
+ *                         the \c p8est_quadrant_init_data routine.
+ * \param [in]  init_fn    Callback function to initialize the user_data
+ *                         which is already allocated automatically.
+ */
+void                p8est_complete_region (p8est_t * p8est,
+                                           const p8est_quadrant_t * q1,
+                                           int include_q1,
+                                           const p8est_quadrant_t * q2,
+                                           int include_q2,
+                                           p8est_tree_t * tree,
+                                           p4est_topidx_t which_tree,
+                                           p8est_init_t init_fn);
+
+/** Completes a sorted tree within a p8est. It may have exterior quadrants.
+ * The completed tree will have only owned quadrants and no overlap.
+ * \param [in,out] p8est      The p8est to work on.
+ * \param [in]     which_tree The 0-based index of the subtree to complete.
+ * \param [in]     init_fn    Callback function to initialize the user_data
+ *                            which is already allocated automatically.
+ */
+void                p8est_complete_subtree (p8est_t * p8est,
+                                            p4est_topidx_t which_tree,
+                                            p8est_init_t init_fn);
+
+/** Balances a sorted tree within a p8est. It may have exterior quadrants.
+ * The completed tree will have only owned quadrants and no overlap.
+ * \param [in,out] p8est      The p8est to work on.
+ * \param [in]     btype      The balance type (face, edge or corner).
+ * \param [in]     which_tree The 0-based index of the subtree to balance.
+ * \param [in]     init_fn    Callback function to initialize the user_data
+ *                            which is already allocated automatically.
+ */
+void                p8est_balance_subtree (p8est_t * p8est,
+                                           p8est_connect_type_t btype,
+                                           p4est_topidx_t which_tree,
+                                           p8est_init_t init_fn);
+
+void                p8est_balance_border (p8est_t * p8est,
+                                          p8est_connect_type_t btype,
+                                          p4est_topidx_t which_tree,
+                                          p8est_init_t init_fn,
+                                          p8est_replace_t replace_fn,
+                                          sc_array_t * borders);
+
+/** Remove overlaps from a sorted list of quadrants.
+ *
+ * This is alogorithm 8 from H. Sundar, R.S. Sampath and G. Biros
+ * with the additional improvement that it works in-place.
+ *
+ * \param [in]     p8est used for the memory pool and quadrant free.
+ * \param [in,out] tree   A sorted tree to be linearized in-place.
+ * \return                Returns the number of removed quadrants.
+ */
+size_t              p8est_linearize_tree (p8est_t * p8est,
+                                          p8est_tree_t * tree);
+
+/** Compute correction of partition for a process.
+ *
+ * The correction denotes how many quadrants the process with id \a rank takes
+ * from (if correction positive) or gives to (if correction negative) the
+ * previous process with id \a rank-1 in order to assign a family of quadrants
+ * to one process.
+ * The process with the highest number of quadrants of a family gets all
+ * quadrants belonging to this family from other processes. If this applies to
+ * several processes, then the process with the lowest id gets the quadrants.
+ * A process can give more quadrants than it owns, if it passes quadrants from
+ * other processes.
+ *
+ * \param [in] partition       first global quadrant index for each process (+1)
+ * \param [in] num_procs       number of processes
+ * \param [in] rank            process id for which correction is computed
+ * \param [in] min_quadrant_id minimal global quadrant index of family
+ * \param [in] max_quadrant_id maximal global quadrant index of family
+ * \return                     correction for process \a rank
+ */
+p4est_locidx_t      p8est_partition_correction (p4est_gloidx_t *
+                                                partition,
+                                                int num_procs,
+                                                int rank,
+                                                p4est_gloidx_t
+                                                min_quadrant_id,
+                                                p4est_gloidx_t
+                                                max_quadrant_id);
+
+/** Correct partition counters to allow one level of coarsening.
+ * No quadrants are actually shipped, just the desired number is updated.
+ * This function guarantees that empty processors remain empty.
+ * This function is collective and acts as a synchronization point.
+ *
+ * \param [in] p8est                     forest whose partition is corrected
+ * \param [in,out] num_quadrants_in_proc partition that will be corrected
+ * \return                               total absolute number of moved
+ *                                       quadrants.  In practice, at most
+ *                                       a small number per processor.
+ */
+p4est_gloidx_t      p8est_partition_for_coarsening (p8est_t * p8est,
+                                                    p4est_locidx_t *
+                                                    num_quadrants_in_proc);
+
+/** Find next non-empty process.
+ *
+ * Finds the next process id >= \a rank which is not empty according to
+ * \a num_quadrants_in_proc.
+ *
+ * \param [in] rank                  process id where search starts
+ * \param [in] num_proc              number of processes
+ * \param [in] num_quadrants_in_proc number of quadrants for each process
+ * \return                           process id of a non empty process
+ */
+int                 p8est_next_nonempty_process (int rank,
+                                                 int num_procs,
+                                                 p4est_locidx_t *
+                                                 num_quadrants_in_proc);
+
+/** Partition \a p8est given the number of quadrants per proc.
+ *
+ * Given the desired number of quadrants per proc \a num_quadrants_in_proc
+ * the forest \a p8est is partitioned.
+ *
+ * \param [in,out] p8est the forest that is partitioned.
+ * \param [in]     num_quadrants_in_proc  an integer array of the number of
+ *                                        quadrants desired per processor.
+ * \return  Returns the global count of shipped quadrants.
+ */
+p4est_gloidx_t      p8est_partition_given (p8est_t * p8est,
+                                           const p4est_locidx_t *
+                                           num_quadrants_in_proc);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_ALGORITHMS_H */
diff --git a/src/p8est_balance.c b/src/p8est_balance.c
new file mode 100644
index 0000000..a789bc4
--- /dev/null
+++ b/src/p8est_balance.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2011 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_balance.c"
diff --git a/src/p8est_balance.h b/src/p8est_balance.h
new file mode 100644
index 0000000..566de71
--- /dev/null
+++ b/src/p8est_balance.h
@@ -0,0 +1,72 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2011 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P8EST_BALANCE_H
+#define P8EST_BALANCE_H
+
+#include <p8est.h>
+
+/** Determines if quadrant \a q causes quadrant \a p to split under the given
+ * \a balance condition.
+ *
+ * \param [in] q         Test quadrant.
+ * \param [in] p         Trial quadrant.
+ * \param [in] balance   Balance condition.
+ * \param [out] seeds    optional array: if \a seeds is not NULL, then it will
+ *                       be resized and filled with descendants of \a p such
+ *                       that the coarsest balanced subtree rooted at \a p
+ *                       that contains all of \a seeds is also the coarset
+ *                       subtree rooted at \a p that is entirely balanced with
+ *                       \a q.
+ * \return               True if \a q causes \a p to split.
+ */
+int                 p8est_balance_seeds (p8est_quadrant_t * q,
+                                         p8est_quadrant_t * p,
+                                         p8est_connect_type_t balance,
+                                         sc_array_t * seeds);
+
+/** Same as p8est_balance_seeds, optimized for the case when it is already
+ * known that \a q is outside of a certain \a face of \a p.
+ */
+int                 p8est_balance_seeds_face (p8est_quadrant_t * q,
+                                              p8est_quadrant_t * p,
+                                              int face, p8est_connect_type_t
+                                              balance, sc_array_t * seeds);
+
+/** Same as p8est_balance_seeds, optimized for the case when it is already
+ * known that \a q is outside of a certain \a edge of \a p.
+ */
+int                 p8est_balance_seeds_edge (p8est_quadrant_t * q,
+                                              p8est_quadrant_t * p,
+                                              int face, p8est_connect_type_t
+                                              balance, sc_array_t * seeds);
+
+/** Same as p8est_balance_seeds, optimized for the case when it is already
+ * known that \a q is outside of a certain \a corner of \a p.
+ */
+int                 p8est_balance_seeds_corner (p8est_quadrant_t * q,
+                                                p8est_quadrant_t * p,
+                                                int face, p8est_connect_type_t
+                                                balance, sc_array_t * seeds);
+
+#endif
diff --git a/src/p8est_bits.c b/src/p8est_bits.c
new file mode 100644
index 0000000..0ccc9d6
--- /dev/null
+++ b/src/p8est_bits.c
@@ -0,0 +1,635 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_bits.c"
+
+int
+p8est_quadrant_is_outside_edge (const p4est_quadrant_t * q)
+{
+  int                 outface[P4EST_DIM];
+
+  outface[0] = (int) (q->x < 0 || q->x >= P4EST_ROOT_LEN);
+  outface[1] = (int) (q->y < 0 || q->y >= P4EST_ROOT_LEN);
+  outface[2] = (int) (q->z < 0 || q->z >= P4EST_ROOT_LEN);
+
+  return outface[0] + outface[1] + outface[2] == 2;
+}
+
+int
+p8est_quadrant_is_outside_edge_extra (const p4est_quadrant_t * q, int *edge)
+{
+  int                 quad_contact[P4EST_FACES];
+  int                 face_axis[P4EST_DIM];
+
+  P4EST_ASSERT (q->level <= P4EST_QMAXLEVEL);
+
+  quad_contact[0] = (int) (q->x < 0);
+  quad_contact[1] = (int) (q->x >= P4EST_ROOT_LEN);
+  quad_contact[2] = (int) (q->y < 0);
+  quad_contact[3] = (int) (q->y >= P4EST_ROOT_LEN);
+  quad_contact[4] = (int) (q->z < 0);
+  quad_contact[5] = (int) (q->z >= P4EST_ROOT_LEN);
+  face_axis[0] = quad_contact[0] || quad_contact[1];
+  face_axis[1] = quad_contact[2] || quad_contact[3];
+  face_axis[2] = quad_contact[4] || quad_contact[5];
+
+  if (face_axis[0] + face_axis[1] + face_axis[2] != 2) {
+    return 0;
+  }
+
+  if (edge != NULL) {
+    if (!face_axis[0]) {
+      *edge = 0 + 2 * quad_contact[5] + quad_contact[3];
+    }
+    else if (!face_axis[1]) {
+      *edge = 4 + 2 * quad_contact[5] + quad_contact[1];
+    }
+    else if (!face_axis[2]) {
+      *edge = 8 + 2 * quad_contact[3] + quad_contact[1];
+    }
+    else {
+      SC_ABORT_NOT_REACHED ();
+    }
+    P4EST_ASSERT (p8est_quadrant_touches_edge (q, *edge, 0));
+  }
+
+  return 1;
+}
+
+int
+p4est_quadrant_is_family (const p4est_quadrant_t * q0,
+                          const p4est_quadrant_t * q1,
+                          const p4est_quadrant_t * q2,
+                          const p4est_quadrant_t * q3,
+                          const p4est_quadrant_t * q4,
+                          const p4est_quadrant_t * q5,
+                          const p4est_quadrant_t * q6,
+                          const p4est_quadrant_t * q7)
+{
+  const int8_t        level = q0->level;
+  p4est_qcoord_t      inc;
+
+  P4EST_ASSERT (p4est_quadrant_is_extended (q0));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q1));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q2));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q3));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q4));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q5));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q6));
+  P4EST_ASSERT (p4est_quadrant_is_extended (q7));
+
+  if (level == 0 || level != q1->level ||
+      level != q2->level || level != q3->level ||
+      level != q4->level || level != q5->level ||
+      level != q6->level || level != q7->level) {
+    return 0;
+  }
+
+  inc = P4EST_QUADRANT_LEN (level);
+  return ((q0->x + inc == q1->x && q0->y == q1->y && q0->z == q1->z) &&
+          (q0->x == q2->x && q0->y + inc == q2->y && q0->z == q2->z) &&
+          (q1->x == q3->x && q2->y == q3->y && q0->z == q3->z) &&
+          (q0->x == q4->x && q0->y == q4->y && q0->z + inc == q4->z) &&
+          (q1->x == q5->x && q1->y == q5->y && q4->z == q5->z) &&
+          (q2->x == q6->x && q2->y == q6->y && q4->z == q6->z) &&
+          (q3->x == q7->x && q3->y == q7->y && q4->z == q7->z));
+}
+
+void
+p8est_quadrant_edge_neighbor (const p4est_quadrant_t * q,
+                              int edge, p4est_quadrant_t * r)
+{
+  const p4est_qcoord_t qh = P4EST_QUADRANT_LEN (q->level);
+
+  P4EST_ASSERT (0 <= edge && edge < 12);
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  switch (edge / 4) {
+  case 0:
+    r->x = q->x;
+    r->y = q->y + (2 * (edge & 0x01) - 1) * qh;
+    r->z = q->z + ((edge & 0x02) - 1) * qh;
+    break;
+  case 1:
+    r->x = q->x + (2 * (edge & 0x01) - 1) * qh;
+    r->y = q->y;
+    r->z = q->z + ((edge & 0x02) - 1) * qh;
+    break;
+  case 2:
+    r->x = q->x + (2 * (edge & 0x01) - 1) * qh;
+    r->y = q->y + ((edge & 0x02) - 1) * qh;
+    r->z = q->z;
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+    break;
+  }
+  r->level = q->level;
+  P4EST_ASSERT (p4est_quadrant_is_extended (r));
+}
+
+void
+p8est_quadrant_edge_neighbor_extra (const p4est_quadrant_t * q, p4est_topidx_t
+                                    t, int edge, sc_array_t * quads,
+                                    sc_array_t * treeids, sc_array_t * nedges,
+                                    p4est_connectivity_t * conn)
+{
+  p4est_quadrant_t    temp;
+  p4est_quadrant_t   *qp;
+  p4est_topidx_t     *tp;
+  int                 face;
+  int                *ip;
+  p8est_edge_info_t   ei;
+  p8est_edge_transform_t *et;
+  sc_array_t         *eta;
+  size_t              etree;
+
+  eta = &ei.edge_transforms;
+
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (quads));
+  P4EST_ASSERT (quads->elem_count == 0);
+  P4EST_ASSERT (quads->elem_size == sizeof (p4est_quadrant_t));
+  P4EST_ASSERT (SC_ARRAY_IS_OWNER (treeids));
+  P4EST_ASSERT (treeids->elem_count == 0);
+  P4EST_ASSERT (treeids->elem_size == sizeof (p4est_topidx_t));
+  if (nedges != NULL) {
+    P4EST_ASSERT (SC_ARRAY_IS_OWNER (nedges));
+    P4EST_ASSERT (nedges->elem_count == 0);
+    P4EST_ASSERT (nedges->elem_size == sizeof (int));
+  }
+
+  p8est_quadrant_edge_neighbor (q, edge, &temp);
+  if (p4est_quadrant_is_inside_root (&temp)) {
+    qp = p4est_quadrant_array_push (quads);
+    *qp = temp;
+    tp = (p4est_topidx_t *) sc_array_push (treeids);
+    *tp = t;
+    if (nedges != NULL) {
+      ip = (int *) sc_array_push (nedges);
+      *ip = (edge ^ 3);
+    }
+    return;
+  }
+
+  if (!p8est_quadrant_is_outside_edge (&temp)) {
+    qp = p4est_quadrant_array_push (quads);
+    tp = (p4est_topidx_t *) sc_array_push (treeids);
+
+    face = p8est_edge_faces[edge][0];
+    p4est_quadrant_face_neighbor (q, face, &temp);
+    if (p4est_quadrant_is_inside_root (&temp)) {
+      face = p8est_edge_faces[edge][1];
+      *tp = p8est_quadrant_face_neighbor_extra (&temp, t, face, qp, NULL,
+                                                conn);
+      if (*tp == -1) {
+        qp = (p4est_quadrant_t *) sc_array_pop (quads);
+        tp = (p4est_topidx_t *) sc_array_pop (treeids);
+      }
+      else if (nedges != NULL) {
+        int                 opedge = (edge ^ 1);
+        int                 nface =
+          conn->tree_to_face[P4EST_FACES * t + face];
+        int                 o = nface / P4EST_FACES;
+        int                 ref, set;
+        int                 c1, c2, nc1, nc2;
+
+        nface = nface % P4EST_FACES;
+
+        P4EST_ASSERT (p8est_edge_faces[opedge][1] == face);
+        ref = p8est_face_permutation_refs[face][nface];
+        set = p8est_face_permutation_sets[ref][o];
+
+        c1 = p8est_edge_corners[opedge][0];
+        nc1 = p8est_corner_face_corners[c1][face];
+        P4EST_ASSERT (nc1 >= 0);
+        c1 = p8est_face_permutations[set][nc1];
+        nc1 = p8est_face_corners[nface][c1];
+
+        c2 = p8est_edge_corners[opedge][1];
+        nc2 = p8est_corner_face_corners[c2][face];
+        P4EST_ASSERT (nc2 >= 0);
+        c2 = p8est_face_permutations[set][nc2];
+        nc2 = p8est_face_corners[nface][c2];
+
+        P4EST_ASSERT (nc1 >= 0);
+        ip = (int *) sc_array_push (nedges);
+        *ip = p8est_child_corner_edges[nc1][nc2];
+        if (nc1 > nc2) {
+          *ip += P8EST_EDGES;
+        }
+        P4EST_ASSERT (*ip >= 0);
+      }
+      return;
+    }
+    face = p8est_edge_faces[edge][1];
+    p4est_quadrant_face_neighbor (q, face, &temp);
+    P4EST_ASSERT (p4est_quadrant_is_inside_root (&temp));
+    face = p8est_edge_faces[edge][0];
+    *tp = p8est_quadrant_face_neighbor_extra (&temp, t, face, qp, NULL, conn);
+    if (*tp == -1) {
+      qp = (p4est_quadrant_t *) sc_array_pop (quads);
+      tp = (p4est_topidx_t *) sc_array_pop (treeids);
+    }
+    else if (nedges != NULL) {
+      int                 opedge = (edge ^ 2);
+      int                 nface = conn->tree_to_face[P4EST_FACES * t + face];
+      int                 o = nface / P4EST_FACES;
+      int                 ref, set;
+      int                 c1, c2, nc1, nc2;
+
+      nface = nface % P4EST_FACES;
+
+      P4EST_ASSERT (p8est_edge_faces[opedge][0] == face);
+      ref = p8est_face_permutation_refs[face][nface];
+      set = p8est_face_permutation_sets[ref][o];
+
+      c1 = p8est_edge_corners[opedge][0];
+      nc1 = p8est_corner_face_corners[c1][face];
+      P4EST_ASSERT (nc1 >= 0);
+      c1 = p8est_face_permutations[set][nc1];
+      nc1 = p8est_face_corners[nface][c1];
+
+      c2 = p8est_edge_corners[opedge][1];
+      nc2 = p8est_corner_face_corners[c2][face];
+      P4EST_ASSERT (nc2 >= 0);
+      c2 = p8est_face_permutations[set][nc2];
+      nc2 = p8est_face_corners[nface][c2];
+
+      P4EST_ASSERT (nc1 >= 0);
+      ip = (int *) sc_array_push (nedges);
+      *ip = p8est_child_corner_edges[nc1][nc2];
+      if (nc1 > nc2) {
+        *ip += P8EST_EDGES;
+      }
+      P4EST_ASSERT (*ip >= 0);
+    }
+    return;
+  }
+  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+  p8est_find_edge_transform (conn, t, edge, &ei);
+  sc_array_resize (quads, eta->elem_count);
+  sc_array_resize (treeids, eta->elem_count);
+  if (nedges != NULL) {
+    sc_array_resize (nedges, eta->elem_count);
+  }
+  for (etree = 0; etree < eta->elem_count; etree++) {
+    qp = p4est_quadrant_array_index (quads, etree);
+    tp = (p4est_topidx_t *) sc_array_index (treeids, etree);
+    et = p8est_edge_array_index (eta, etree);
+    p8est_quadrant_transform_edge (&temp, qp, &ei, et, 1);
+    *tp = et->ntree;
+    if (nedges != NULL) {
+      ip = (int *) sc_array_index (nedges, etree);
+      *ip = et->nedge;
+      if (et->nflip) {
+        *ip += P8EST_EDGES;
+      }
+    }
+  }
+  sc_array_reset (eta);
+}
+
+int
+p8est_quadrant_touches_edge (const p4est_quadrant_t * q, int edge, int inside)
+{
+  int                 quad_contact[P4EST_FACES];
+  int                 axis, side, incount;
+  p4est_qcoord_t      lower, upper;
+
+  P4EST_ASSERT (0 <= edge && edge < 12);
+
+  axis = edge / 4;
+  if (q->level == P4EST_MAXLEVEL) {
+    P4EST_ASSERT (p4est_quadrant_is_node (q, inside));
+    lower = 0;
+    upper = P4EST_ROOT_LEN - (int) inside;
+  }
+  else {
+    if (!inside) {
+      P4EST_ASSERT (p4est_quadrant_is_extended (q));
+      lower = -P4EST_QUADRANT_LEN (q->level);
+      upper = P4EST_ROOT_LEN;
+    }
+    else {
+      P4EST_ASSERT (p4est_quadrant_is_valid (q));
+      lower = 0;
+      upper = P4EST_LAST_OFFSET (q->level);
+    }
+  }
+  quad_contact[0] = (q->x == lower);
+  quad_contact[1] = (q->x == upper);
+  quad_contact[2] = (q->y == lower);
+  quad_contact[3] = (q->y == upper);
+  quad_contact[4] = (q->z == lower);
+  quad_contact[5] = (q->z == upper);
+
+  incount = 0;
+  if (axis != 0) {
+    side = edge & 1;
+    incount += quad_contact[side];
+  }
+  if (axis != 1) {
+    side = (axis == 0) ? (edge & 1) : ((edge >> 1) & 1);
+    incount += quad_contact[2 + side];
+  }
+  if (axis != 2) {
+    side = (edge >> 1) & 1;
+    incount += quad_contact[4 + side];
+  }
+#ifdef P4EST_ENABLE_DEBUG
+  upper = P4EST_ROOT_LEN + (p4est_qcoord_t) (q->level == P4EST_MAXLEVEL
+                                             && !inside);
+  P4EST_ASSERT (axis != 0 || (q->x >= 0 && q->x < upper));
+  P4EST_ASSERT (axis != 1 || (q->y >= 0 && q->y < upper));
+  P4EST_ASSERT (axis != 2 || (q->z >= 0 && q->z < upper));
+#endif
+
+  return incount == 2;
+}
+
+void
+p8est_quadrant_transform_edge (const p4est_quadrant_t * q,
+                               p4est_quadrant_t * r,
+                               const p8est_edge_info_t * ei,
+                               const p8est_edge_transform_t * et, int inside)
+{
+  int                 iaxis;
+  p4est_qcoord_t      mh, Rmh;
+  p4est_qcoord_t      lshift, rshift;
+  p4est_qcoord_t      my_xyz, *target_xyz[3];
+
+  iaxis = (int) ei->iedge / 4;
+  P4EST_ASSERT (0 <= et->naxis[0] && et->naxis[0] < 3);
+  P4EST_ASSERT (0 <= et->naxis[1] && et->naxis[1] < 3);
+  P4EST_ASSERT (0 <= et->naxis[2] && et->naxis[2] < 3);
+  P4EST_ASSERT (et->naxis[0] != et->naxis[1] &&
+                et->naxis[0] != et->naxis[2] && et->naxis[1] != et->naxis[2]);
+  P4EST_ASSERT (0 <= et->nflip && et->nflip < 2);
+  P4EST_ASSERT (q != r);
+
+  if (q->level == P4EST_MAXLEVEL) {
+    P4EST_ASSERT (!inside);
+    P4EST_ASSERT (p8est_quadrant_touches_edge (q, (int) ei->iedge, inside));
+    lshift = mh = 0;
+    rshift = Rmh = P4EST_ROOT_LEN;
+  }
+  else {
+    P4EST_ASSERT (p8est_quadrant_touches_edge (q, (int) ei->iedge, !inside));
+    mh = -P4EST_QUADRANT_LEN (q->level);
+    Rmh = P4EST_ROOT_LEN + mh;
+    lshift = (inside ? 0 : mh);
+    rshift = (inside ? Rmh : P4EST_ROOT_LEN);
+  }
+  target_xyz[0] = &r->x;
+  target_xyz[1] = &r->y;
+  target_xyz[2] = &r->z;
+
+  /* transform coordinate axis parallel to edge */
+  switch (iaxis) {
+  case 0:
+    my_xyz = q->x;
+    break;
+  case 1:
+    my_xyz = q->y;
+    break;
+  case 2:
+    my_xyz = q->z;
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+  if (!et->nflip) {
+    *target_xyz[et->naxis[0]] = my_xyz;
+  }
+  else {
+    *target_xyz[et->naxis[0]] = Rmh - my_xyz;
+  }
+
+  /* create the other two coordinates */
+  switch (et->corners) {
+  case 0:
+    *target_xyz[et->naxis[1]] = lshift;
+    *target_xyz[et->naxis[2]] = lshift;
+    break;
+  case 1:
+    *target_xyz[et->naxis[1]] = rshift;
+    *target_xyz[et->naxis[2]] = lshift;
+    break;
+  case 2:
+    *target_xyz[et->naxis[1]] = lshift;
+    *target_xyz[et->naxis[2]] = rshift;
+    break;
+  case 3:
+    *target_xyz[et->naxis[1]] = rshift;
+    *target_xyz[et->naxis[2]] = rshift;
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+
+#ifdef P4EST_ENABLE_DEBUG
+  {
+    /* This is the code from the paper. */
+
+    p4est_qcoord_t      qparallel, qt1, qt2;
+
+    qparallel = et->nflip * Rmh + (1 - 2 * et->nflip) * my_xyz;
+    P4EST_ASSERT (qparallel == *target_xyz[et->naxis[0]]);
+
+    qt1 = (!(et->nedge & 1)) ? lshift : rshift;
+    qt2 = (!(et->nedge & 2)) ? lshift : rshift;
+    P4EST_ASSERT (qt1 == *target_xyz[et->naxis[1]]);
+    P4EST_ASSERT (qt2 == *target_xyz[et->naxis[2]]);
+  }
+#endif
+
+  r->level = q->level;
+  P4EST_ASSERT (p8est_quadrant_touches_edge (r, (int) et->nedge, inside));
+}
+
+void
+p8est_quadrant_shift_edge (const p4est_quadrant_t * q,
+                           p4est_quadrant_t * r, p4est_quadrant_t * rup,
+                           p4est_quadrant_t * rdown, int edge)
+{
+  int                 outface;
+  int                 i, level;
+  int                 cid, sid[P4EST_DIM], step[P4EST_DIM];
+  p4est_qcoord_t      th;
+  p4est_quadrant_t    quad[P4EST_DIM];
+  /* *INDENT-OFF* */
+  const int           contact[12] = {
+    0x14, 0x18, 0x24, 0x28,
+    0x11, 0x12, 0x21, 0x22,
+    0x05, 0x06, 0x09, 0x0a
+  };
+  /* *INDENT-ON* */
+
+  P4EST_ASSERT (q != r);
+  P4EST_ASSERT (rup == NULL || q != rup);
+  P4EST_ASSERT (rdown == NULL || q != rdown);
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+  P4EST_ASSERT (edge >= 0 && edge < 12);
+
+  P4EST_QUADRANT_INIT (&quad[0]);
+  P4EST_QUADRANT_INIT (&quad[1]);
+  P4EST_QUADRANT_INIT (&quad[2]);
+
+  quad[0] = *q;
+  quad[1] = *q;
+  quad[2] = *q;
+  for (;;) {
+    th = P4EST_LAST_OFFSET (quad[0].level);
+    cid = p4est_quadrant_child_id (&quad[1]);
+    switch (edge / 4) {
+    case 0:
+      sid[0] = 2 * edge;
+      sid[1] = 2 * edge + (cid & 0x01);
+      sid[2] = 2 * edge + 1;
+      step[0] = 0;
+      step[1] = 2 * (edge & 0x01) - 1;
+      step[2] = (edge & 0x02) - 1;
+      break;
+    case 1:
+      sid[0] = 2 * (edge & 0x02) + (edge & 0x01);
+      sid[1] = 2 * (edge & 0x02) + (edge & 0x01) + (cid & 0x02);
+      sid[2] = 2 * (edge & 0x02) + (edge & 0x01) + 2;
+      step[0] = 2 * (edge & 0x01) - 1;
+      step[1] = 0;
+      step[2] = (edge & 0x02) - 1;
+      break;
+    case 2:
+      sid[0] = edge - 8;
+      sid[1] = edge - 8 + (cid & 0x04);
+      sid[2] = edge - 8 + 4;
+      step[0] = 2 * (edge & 0x01) - 1;
+      step[1] = (edge & 0x02) - 1;
+      step[2] = 0;
+      break;
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+    p4est_quadrant_sibling (&quad[1], r, sid[1]);
+    if (rup != NULL) {
+      p4est_quadrant_sibling (&quad[0], rup, sid[0]);
+    }
+    if (rdown != NULL) {
+      p4est_quadrant_sibling (&quad[2], rdown, sid[2]);
+    }
+    P4EST_ASSERT (-1 <= step[0] && step[0] <= 1);
+    P4EST_ASSERT (-1 <= step[1] && step[1] <= 1);
+    P4EST_ASSERT (-1 <= step[2] && step[2] <= 1);
+
+    outface = 0;
+    if (step[0] != 0) {
+      outface |= ((r->x <= 0) ? 0x01 : 0);
+      outface |= ((r->x >= th) ? 0x02 : 0);
+    }
+    if (step[1] != 0) {
+      outface |= ((r->y <= 0) ? 0x04 : 0);
+      outface |= ((r->y >= th) ? 0x08 : 0);
+    }
+    if (step[2] != 0) {
+      outface |= ((r->z <= 0) ? 0x10 : 0);
+      outface |= ((r->z >= th) ? 0x20 : 0);
+    }
+    if (outface == contact[edge]) {
+      break;
+    }
+    level = quad[0].level - 1;
+    for (i = 0; i < P4EST_DIM; i++) {
+      p4est_quadrant_parent (&quad[i], &quad[i]);
+      quad[i].x += (p4est_qcoord_t) step[0] * P4EST_QUADRANT_LEN (level);
+      quad[i].y += (p4est_qcoord_t) step[1] * P4EST_QUADRANT_LEN (level);
+      quad[i].z += (p4est_qcoord_t) step[2] * P4EST_QUADRANT_LEN (level);
+    }
+    switch (edge / 4) {
+    case 0:
+      quad[0].x += P4EST_QUADRANT_LEN (level);
+      quad[2].x -= P4EST_QUADRANT_LEN (level);
+      break;
+    case 1:
+      quad[0].y += P4EST_QUADRANT_LEN (level);
+      quad[2].y -= P4EST_QUADRANT_LEN (level);
+      break;
+    case 2:
+      quad[0].z += P4EST_QUADRANT_LEN (level);
+      quad[2].z -= P4EST_QUADRANT_LEN (level);
+      break;
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+
+    P4EST_ASSERT (p4est_quadrant_is_extended (&quad[0]));
+    P4EST_ASSERT (p4est_quadrant_is_extended (&quad[1]));
+    P4EST_ASSERT (p4est_quadrant_is_extended (&quad[2]));
+  }
+
+  if (step[0] != 0) {
+    if (r->x < 0)
+      r->x = 0;
+    if (r->x >= P4EST_ROOT_LEN)
+      r->x = th;
+  }
+  if (rup != NULL && rup->x < 0)
+    rup->x = 0;
+  if (rup != NULL && rup->x >= P4EST_ROOT_LEN)
+    rup->x = th;
+  if (rdown != NULL && rdown->x < 0)
+    rdown->x = 0;
+  if (rdown != NULL && rdown->x >= P4EST_ROOT_LEN)
+    rdown->x = th;
+  if (step[1] != 0) {
+    if (r->y < 0)
+      r->y = 0;
+    if (r->y >= P4EST_ROOT_LEN)
+      r->y = th;
+  }
+  if (rup != NULL && rup->y < 0)
+    rup->y = 0;
+  if (rup != NULL && rup->y >= P4EST_ROOT_LEN)
+    rup->y = th;
+  if (rdown != NULL && rdown->y < 0)
+    rdown->y = 0;
+  if (rdown != NULL && rdown->y >= P4EST_ROOT_LEN)
+    rdown->y = th;
+  if (step[2] != 0) {
+    if (r->z < 0)
+      r->z = 0;
+    if (r->z >= P4EST_ROOT_LEN)
+      r->z = th;
+  }
+  if (rup != NULL && rup->z < 0)
+    rup->z = 0;
+  if (rup != NULL && rup->z >= P4EST_ROOT_LEN)
+    rup->z = th;
+  if (rdown != NULL && rdown->z < 0)
+    rdown->z = 0;
+  if (rdown != NULL && rdown->z >= P4EST_ROOT_LEN)
+    rdown->z = th;
+  P4EST_ASSERT (p8est_quadrant_touches_edge (r, edge, 1));
+  P4EST_ASSERT (rup == NULL || p8est_quadrant_touches_edge (rup, edge, 1));
+  P4EST_ASSERT (rdown == NULL
+                || p8est_quadrant_touches_edge (rdown, edge, 1));
+}
diff --git a/src/p8est_bits.h b/src/p8est_bits.h
new file mode 100644
index 0000000..49f6088
--- /dev/null
+++ b/src/p8est_bits.h
@@ -0,0 +1,706 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_bits.h
+ *
+ * Routines for manipulating quadrants (neighbors, parents, children, etc.)
+ *
+ * \ingroup p8est
+ */
+
+#ifndef P8EST_BITS_H
+#define P8EST_BITS_H
+
+#include <p8est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Prints one line with quadrant's x, y, z and level.
+ * \param [in] log_priority  see \ref logpriorities in sc.h for the meanings
+ *                           of numerical priority values
+ * \param [in] q             quadrant to print
+ */
+void                p8est_quadrant_print (int log_priority,
+                                          const p8est_quadrant_t * q);
+
+/** Test if two quadrants have equal Morton indices.
+ * \return true if \a q1 describes the same quadrant as \a q2.
+ */
+int                 p8est_quadrant_is_equal (const p8est_quadrant_t * q1,
+                                             const p8est_quadrant_t * q2);
+
+/** Test if two quadrants overlap.
+ * \return true if \a q1 and \a q2 are equal or one is the ancestor of the
+ * other.
+ */
+int                 p8est_quadrant_overlaps (const p8est_quadrant_t * q1,
+                                             const p8est_quadrant_t * q2);
+
+/** Test if two quadrants have equal Morton indices and the same tree id.
+ * \return          true if \a q1 describes the same quadrant as \a q2
+ *                  and the p.which_tree fields are equal.
+ */
+int                 p8est_quadrant_is_equal_piggy (const p8est_quadrant_t *
+                                                   q1,
+                                                   const p8est_quadrant_t *
+                                                   q2);
+
+/** Compare two quadrants in their Morton ordering.
+ * Both quadrants must be valid.
+ * \return Returns < 0 if \a v1 < \a v2,
+ *                   0 if \a v1 == \a v2,
+ *                 > 0 if \a v1 > \a v2
+ */
+int                 p8est_quadrant_compare (const void *v1, const void *v2);
+
+/** Compare two quadrants in their Morton ordering, with equivalence if the
+ * two quadrants overlap.
+ * \return Returns < 0 if \a v1 < \a v2 and \a v1 and \v2 do not overlap,
+ *                   0 if \a v1 and \a v2 overlap,
+ *                 > 0 if \a v1 > \a v2 and \a v1 and \v2 do not overlap.
+ */
+int                 p8est_quadrant_disjoint (const void *v1, const void *v2);
+
+/** Compare two quadrants in their Morton ordering and the which_tree member.
+ * Both quadrants must be extended (superset of valid, see below).
+ * \return Returns < 0 if \a v1 < \a v2,
+ *                   0 if \a v1 == \a v2,
+ *                 > 0 if \a v1 > \a v2
+ */
+int                 p8est_quadrant_compare_piggy (const void *v1,
+                                                  const void *v2);
+
+/** Compare two quadrants with respect to their local_num in the piggy3 member.
+ * \return Returns < 0 if \a v1 < \a v2,
+ *                   0 if \a v1 == \a v2,
+ *                 > 0 if \a v1 > \a v2
+ */
+int                 p8est_quadrant_compare_local_num (const void *v1,
+                                                      const void *v2);
+
+/** Test if two quadrants have equal Morton indices, callback version.
+ * \return true if \a v1 describes the same quadrant as \a v2.
+ */
+int                 p8est_quadrant_equal_fn (const void *v1, const void *v2,
+                                             const void *u);
+
+/** Computes a hash value for a quadrant by the lookup3 method.
+ */
+unsigned            p8est_quadrant_hash_fn (const void *v, const void *u);
+
+/** Test if two nodes are in the same tree and have equal Morton indices.
+ * \param [in] v1   Pointer to a clamped or unclamped node, depending on u.
+ * \param [in] v2   Pointer to a clamped or unclamped node, depending on u.
+ * \param [in] u    User data, points to an int holding the clamped-flag.
+ */
+int                 p8est_node_equal_piggy_fn (const void *v1,
+                                               const void *v2, const void *u);
+
+/** Compute hash value of a node based on its tree and Morton index.
+ * \param [in] v    Pointer to a clamped or unclamped node, depending on u.
+ * \param [in] u    User data, points to an int holding the clamped-flag.
+ */
+unsigned            p8est_node_hash_piggy_fn (const void *v, const void *u);
+
+/** Clamp a node inside the unit tree if it sits on a high border.
+ * \param [in] n    Node to be clamped. Must not yet be clamped.
+ * \param [out] r   Existing node overwritten by the clamped result.
+ */
+void                p8est_node_clamp_inside (const p8est_quadrant_t * n,
+                                             p8est_quadrant_t * r);
+
+/** Move a clamped node out on the border.
+ * \param [in] n    Node to be unclamped in-place.
+ */
+void                p8est_node_unclamp (p8est_quadrant_t * n);
+
+/** Find the enclosing quadrant of a given node at a given level.
+ * \param [in] n        Clamped node.
+ * \param [in] level    Level of the quadrant to be created.
+ * \param [out] q       Output quadrant, n == q is permitted.
+ */
+void                p8est_node_to_quadrant (const p8est_quadrant_t * n,
+                                            int level, p8est_quadrant_t * q);
+
+/** Decide if a node is completely contained within a quadrant.
+ * \param [in] q        Valid quadrant.
+ * \param [in] n        Clamped node.
+ */
+int                 p8est_quadrant_contains_node (const p8est_quadrant_t * q,
+                                                  const p8est_quadrant_t * n);
+
+/** Compute the position of the ancestor of this child at level \a level within
+ * its siblings.
+ * \return Returns its child id in 0..7
+ */
+int                 p8est_quadrant_ancestor_id (const p8est_quadrant_t * q,
+                                                int level);
+
+/** Compute the position of this child within its siblings.
+ * \return Returns its child id in 0..7
+ */
+int                 p8est_quadrant_child_id (const p8est_quadrant_t * q);
+
+/** Test if a quadrant is inside the unit tree.
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is inside the unit tree.
+ */
+int                 p8est_quadrant_is_inside_root (const p8est_quadrant_t *
+                                                   q);
+
+/** Test if a quadrant is inside the 3x3 box around the root tree.
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is inside the unit tree.
+ */
+int                 p8est_quadrant_is_inside_3x3 (const p8est_quadrant_t * q);
+
+/** Test if a quadrant is outside a tree face boundary (no edge or corner).
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is outside across a unit tree face.
+ */
+int                 p8est_quadrant_is_outside_face (const p8est_quadrant_t *
+                                                    q);
+
+/** Test if a quadrant is outside a tree edge boundary (no corner).
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is outside across a unit tree edge.
+ */
+int                 p8est_quadrant_is_outside_edge (const p8est_quadrant_t *
+                                                    q);
+
+/** Test if a quadrant is outside a tree edge boundary (no corner).
+ * \param [in] q       Quadrant to be tested.
+ * \param [out] edge   The tree edge number is computed if outside edge.
+ *                     This pointer may be NULL.
+ * \return Returns true if \a q is outside across a unit tree edge.
+ */
+int                 p8est_quadrant_is_outside_edge_extra (const
+                                                          p8est_quadrant_t *
+                                                          q, int *edge);
+
+/** Test if a quadrant is outside a tree corner boundary.
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is outside across a unit tree corner.
+ */
+int                 p8est_quadrant_is_outside_corner (const p8est_quadrant_t *
+                                                      q);
+
+/** Test if a quadrant is used to represent a mesh node.
+ * \param [in] q        Quadrant to be tested.
+ * \param [in] inside   If true, boundary nodes must be clamped inside.
+ *                      If false, nodes must align with the quadrant grid.
+ * \return Returns true if \a q is a node.
+ */
+int                 p8est_quadrant_is_node (const p8est_quadrant_t * q,
+                                            int inside);
+
+/** Test if a quadrant has valid Morton indices and is inside the unit tree.
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is valid.
+ */
+int                 p8est_quadrant_is_valid (const p8est_quadrant_t * q);
+
+/** Test if a quadrant has valid Morton indices in the 3x3 box around root.
+ * \param [in] q Quadrant to be tested.
+ * \return Returns true if \a q is extended.
+ */
+int                 p8est_quadrant_is_extended (const p8est_quadrant_t * q);
+
+/** Test if two quadrants are siblings.
+ * \param [in] q1 First quadrant to be tested.
+ * \param [in] q2 Second quadrant to be tested.
+ * \return true if \a q1 is unequal to and a sibling of \a q2.
+ */
+int                 p8est_quadrant_is_sibling (const p8est_quadrant_t * q1,
+                                               const p8est_quadrant_t * q2);
+
+/** Test if two quadrants are siblings.
+ * Descriptive, slower version of \a p8est_quadrant_is_sibling.
+ * For debugging and educational purposes only.
+ */
+int                 p8est_quadrant_is_sibling_D (const p8est_quadrant_t * q1,
+                                                 const p8est_quadrant_t * q2);
+
+/** Test if 8 quadrants are siblings in Morton ordering.
+ */
+int                 p8est_quadrant_is_family (const p8est_quadrant_t * q0,
+                                              const p8est_quadrant_t * q1,
+                                              const p8est_quadrant_t * q2,
+                                              const p8est_quadrant_t * q3,
+                                              const p8est_quadrant_t * q4,
+                                              const p8est_quadrant_t * q5,
+                                              const p8est_quadrant_t * q6,
+                                              const p8est_quadrant_t * q7);
+
+/** Test if 8 quadrants are siblings in Morton ordering, array version.
+ * \param [in] q   Array of 8 quadrants.
+ */
+int                 p8est_quadrant_is_familyv (const p8est_quadrant_t q[]);
+
+/** Test if 8 quadrants are siblings in Morton ordering, array version.
+ * \param [in] q   Array of 8 pointers to quadrants.
+ */
+int                 p8est_quadrant_is_familypv (p8est_quadrant_t * q[]);
+
+/** Test if a quadrant is the parent of another quadrant.
+ * \param [in] q Quadrant to be tested.
+ * \param [in] r Possible child quadrant.
+ * \return true if \a q is the parent of \a r.
+ */
+int                 p8est_quadrant_is_parent (const p8est_quadrant_t * q,
+                                              const p8est_quadrant_t * r);
+
+/** Test if a quadrant is the parent of another quadrant.
+ * Descriptive, slower version of \a p8est_quadrant_is_parent.
+ * For debugging and educational purposes only.
+ */
+int                 p8est_quadrant_is_parent_D (const p8est_quadrant_t * q,
+                                                const p8est_quadrant_t * r);
+
+/** Test if a quadrant is an ancestor of another quadrant.
+ * \param [in] q Quadrant to be tested.
+ * \param [in] r Descendent quadrant.
+ * \return true if \a q is unequal to and an ancestor of \a r.
+ */
+int                 p8est_quadrant_is_ancestor (const p8est_quadrant_t * q,
+                                                const p8est_quadrant_t * r);
+
+/** Test if a quadrant is an ancestor of another quadrant.
+ * Descriptive, slower version of \a p8est_quadrant_is_ancestor.
+ * Contrary to \a p8est_quadrant_is_ancestor, it aborts for inter-tree q, r.
+ * For debugging and educational purposes only.
+ */
+int                 p8est_quadrant_is_ancestor_D (const p8est_quadrant_t * q,
+                                                  const p8est_quadrant_t * r);
+
+/** Test if two quadrants follow each other in the tree with no holes.
+ * \param [in] q A quadrant
+ * \param [in] r Another quadrant
+ * \return true if \a q is immediately before \a r in the tree.
+ * \note for every \a q there are between 0 and P8EST_MAXLEVEL+1 possible nexts.
+ */
+int                 p8est_quadrant_is_next (const p8est_quadrant_t * q,
+                                            const p8est_quadrant_t * r);
+
+/** Test if two quadrants follow each other in the tree with no holes.
+ * Descriptive, slower version of \a p8est_quadrant_is_next.
+ * For debugging and educational purposes only.
+ */
+int                 p8est_quadrant_is_next_D (const p8est_quadrant_t * q,
+                                              const p8est_quadrant_t * r);
+
+/** Test if a quadrant has at least partial overlap with a tree.
+ */
+int                 p8est_quadrant_overlaps_tree (p8est_tree_t * tree,
+                                                  const p8est_quadrant_t * q);
+
+/** Test if a quadrant is completely contained within a tree.
+ */
+int                 p8est_quadrant_is_inside_tree (p8est_tree_t * tree,
+                                                   const p8est_quadrant_t *
+                                                   q);
+
+/** Compute the ancestor of a quadrant at a given level.
+ * \param [in]  q       Input quadrant.
+ * \param [in]  level   A smaller level than q.
+ * \param [in,out]  r   Existing quadrent whose Morton index will be filled
+ *                      with the ancestor of q at the given level.
+ * \note The quadrant q may point to the same quadrant as r.
+ *       The user_data of r are never modified.
+ */
+void                p8est_quadrant_ancestor (const p8est_quadrant_t * q,
+                                             int level, p8est_quadrant_t * r);
+
+/** Compute the parent of a quadrant.
+ * \param [in]  q Input quadrant.
+ * \param [in,out] r Existing quadrant whose Morton index will be filled
+ *                   with the Morton index of the parent of \a q.
+ *                   Its user_data will be untouched.
+ * \note \a q may point to the same quadrant as \a r.
+         The user_data of \a r is never modified.
+ */
+void                p8est_quadrant_parent (const p8est_quadrant_t * q,
+                                           p8est_quadrant_t * r);
+
+/** Compute a specific sibling of a quadrant.
+ * \param [in]     q  Input quadrant.
+ * \param [in,out] r  Existing quadrant whose Morton index will be filled
+ *                    with the coordinates of sibling no. sibling_id of q.
+ * \param [in]     sibling_id The id of the sibling computed, 0..3.
+ */
+void                p8est_quadrant_sibling (const p8est_quadrant_t * q,
+                                            p8est_quadrant_t * r,
+                                            int sibling_id);
+
+/** Compute the face neighbor of a quadrant.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     face   The face across which to generate the neighbor.
+ * \param [in,out] r      Existing quadrant whose Morton index will be filled.
+ * \note \a q may point to the same quadrant as \a r.
+ */
+void                p8est_quadrant_face_neighbor (const p8est_quadrant_t * q,
+                                                  int face,
+                                                  p8est_quadrant_t * r);
+
+/** Compute the face neighbor of a quadrant, transforming across tree
+ * boundaries if necessary.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     t      Tree that contains \q.
+ * \param [in]     face   The face across which to generate the neighbor.
+ * \param [in,out] r      Existing quadrant whose Morton index will be filled.
+ *                        By convention, if there is no tree across \face,
+ *                        \r has the same Morton index as \q.
+ * \param [in,out] nface  if not NULL, set to the face of \r that neighbors
+ *                        \q.  nface is encoded with orientation information
+ *                        in the same manner as the tree_to_face array in
+ *                        the p8est_connectivity_t struct.
+ * \param [in]     conn   The connectivity structure for the forest.
+ * \return Returns the tree that contains \r.  By convention, if there is no
+ * tree across \face, then -1 is returned.
+ */
+p4est_locidx_t      p8est_quadrant_face_neighbor_extra (const p8est_quadrant_t
+                                                        * q, p4est_topidx_t t,
+                                                        int face,
+                                                        p8est_quadrant_t * r,
+                                                        int *nface,
+                                                        p8est_connectivity_t *
+                                                        conn);
+
+/** Get the smaller face neighbors of \a q.
+ *
+ * Gets the smaller face neighbors, which are half of the size assuming the
+ * 2-1 constant.
+ *
+ * The order of the \a n[i] is given in the Morton ordering.
+ *
+ * \param [in]  q      The quadrant whose face neighbors will be constructed.
+ * \param [in]  face   The face across which to generate the neighbors.
+ * \param [out] n[0]..n[3] Filled with the four smaller face neighbors.
+ * \param [out] nur[0]..nur[3] If not NULL, filled with smallest quadrants
+ *                     that fit in the upper right corners of \a n.
+ */
+void                p8est_quadrant_half_face_neighbors (const p8est_quadrant_t
+                                                        * q, int face,
+                                                        p8est_quadrant_t n[],
+                                                        p8est_quadrant_t
+                                                        nur[]);
+
+/** Create all possible face neighbors of \a q.
+ *
+ * Gets the face neighbors, possible assuming the 2-1 constraint.
+ * If the larger or smaller quadrants do not exist than they are returned
+ * as initialized by P4EST_QUADRANT_INIT.
+ *
+ * The order of \a n[0] through \a n[3] are given in Morton ordering.
+ *
+ * \param [in]  q      The quadrant whose face neighbors will be constructed.
+ * \param [in]  face   The face across which to generate the neighbors.
+ * \param [out] n[0]..n[3] Filled with the smaller possible face neighbors,
+ *                     which are half of the size if they exist
+ *                     or initialized to P4EST_QUADRANT_INIT.
+ * \param [out] n[4]   Filled with the face neighbor, which is the same size.
+ * \param [out] n[5]   Filled with the face neighbor, which is twice the size
+ *                     if it exists or initialized to P4EST_QUADRANT_INIT.
+ */
+void                p8est_quadrant_all_face_neighbors (const p8est_quadrant_t
+                                                       * q, int face,
+                                                       p8est_quadrant_t n[]);
+
+/** Compute the edge neighbor of a quadrant.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     edge   The edge across which to generate the neighbor.
+ * \param [in,out] r      Existing quadrant whose Morton index will be filled.
+ * \note \a q may point to the same quadrant as \a r.
+ */
+void                p8est_quadrant_edge_neighbor (const p8est_quadrant_t * q,
+                                                  int edge,
+                                                  p8est_quadrant_t * r);
+
+/** Compute the edge neighbors of a quadrant, transforming across tree
+ * boundaries if necessary.  Only computes neighbors that are not face
+ * neighbors.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     t      Tree that contains \q.
+ * \param [in]     edge   The edge across which to generate the neighbor.
+ * \param [in,out] quads  An initialized but empty array where the edge
+ *                        neighbors will be placed.
+ * \param [in,out] treeids An initialized but empty array where the ids of the
+ *                        trees containing the edge neighbors will be placed.
+ * \param [in,out] nedges if not NULL, filled with the edges of \a quads that
+ *                        neighbor \q. the ints in \nedges are encoded with
+ *                        orientation informatin like the edge_to_edge array
+ *                        in the p8est_connectivity_t struct
+ * \param [in]     conn   The connectivity structure for the forest.
+ */
+void                p8est_quadrant_edge_neighbor_extra (const p8est_quadrant_t
+                                                        * q, p4est_locidx_t t,
+                                                        int edge, sc_array_t *
+                                                        quads, sc_array_t *
+                                                        treeids,
+                                                        sc_array_t * nedges,
+                                                        p8est_connectivity_t *
+                                                        conn);
+
+/** Compute the corner neighbor of a quadrant.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     corner The corner across which to generate the neighbor.
+ * \param [in,out] r      Existing quadrant whose Morton index will be filled.
+ * \note \a q may point to the same quadrant as \a r.
+ */
+void                p8est_quadrant_corner_neighbor (const p8est_quadrant_t *
+                                                    q, int corner,
+                                                    p8est_quadrant_t * r);
+
+/** Compute the corner neighbors of a quadrant, transforming across tree
+ * boundaries if necessary.  Only computes neighbors that are not face or edge
+ * neighbors.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     t      Tree that contains \q.
+ * \param [in]     corner The corner across which to generate the neighbor.
+ * \param [in,out] quads  An initialized but empty array where the corner
+ *                        neighbors will be placed.
+ * \param [in,out] treeids An initialized but empty array where the ids of the
+ *                        trees containing the corner neighbors will be placed.
+ * \param [in,out] ncorners if not NULL, filled with the corners of \a quads
+ *                          that neighbor \q.
+ * \param [in]     conn   The connectivity structure for the forest.
+ */
+void                p8est_quadrant_corner_neighbor_extra (const
+                                                          p8est_quadrant_t *
+                                                          q, p4est_locidx_t t,
+                                                          int corner,
+                                                          sc_array_t * quads,
+                                                          sc_array_t *
+                                                          treeids,
+                                                          sc_array_t *
+                                                          ncorners,
+                                                          p8est_connectivity_t
+                                                          * conn);
+
+/** Compute the half size corner neighbor of a quadrant.
+ *
+ * \param [in]  q       The quadrant whose corner neighbor will be constructed.
+ * \param [in]  corner  The corner across which to generate the neighbor.
+ * \param [out] r       Morton index filled with the half size corner neighbor.
+ */
+void                p8est_quadrant_half_corner_neighbor (const
+                                                         p8est_quadrant_t * q,
+                                                         int corner,
+                                                         p8est_quadrant_t *
+                                                         r);
+
+/** Compute the corner node of a quadrant.
+ * \param [in]     q      Input quadrant, must be valid.
+ * \param [in]     corner The corner across which to generate the neighbor.
+ * \param [in,out] r      Node that will not be clamped inside.
+ * \note \a q may point to the same quadrant as \a r.
+ */
+void                p8est_quadrant_corner_node (const p8est_quadrant_t * q,
+                                                int corner,
+                                                p8est_quadrant_t * r);
+
+/** Compute the 8 children of a quadrant.
+ * \param [in]     q  Input quadrant.
+ * \param [in,out] c0 First computed child.
+ *                    \a q may point to the same quadrant as \a c0.
+ * \note The user_data of \a c0, c1, c2, c3, c4, c5, c6, c7 is never modified.
+ */
+void                p8est_quadrant_children (const p8est_quadrant_t * q,
+                                             p8est_quadrant_t * c0,
+                                             p8est_quadrant_t * c1,
+                                             p8est_quadrant_t * c2,
+                                             p8est_quadrant_t * c3,
+                                             p8est_quadrant_t * c4,
+                                             p8est_quadrant_t * c5,
+                                             p8est_quadrant_t * c6,
+                                             p8est_quadrant_t * c7);
+
+/** Compute the 8 children of a quadrant, array version.
+ * \param [in]     q  Input quadrant.
+ * \param [in,out] c  The 8 computed children in z-order.
+ *                    q may point to the same quadrant as c[0].
+ * \note The user_data of c[i] is never modified.
+ */
+void                p8est_quadrant_childrenv (const p8est_quadrant_t * q,
+                                              p8est_quadrant_t c[]);
+
+/** Compute the 8 children of a quadrant, array version.
+ * \param [in]     q  Input quadrant.
+ * \param [in,out] c  Pointers to the 8 computed children in z-order.
+ *                    q may point to the same quadrant as c[0].
+ * \note The user_data of c[i] is never modified.
+ */
+void                p8est_quadrant_childrenpv (const p8est_quadrant_t * q,
+                                               p8est_quadrant_t * c[]);
+
+/** Compute the first descendant of a quadrant on a given level.
+ * \param [in]  q      Input quadrant.
+ * \param [out] fd     First descendant of \a q on level \a level.
+ * \param [in]  level  Level must be greater equal than q's level.
+ */
+void                p8est_quadrant_first_descendant (const p8est_quadrant_t *
+                                                     q, p8est_quadrant_t * fd,
+                                                     int level);
+
+/** Compute the last descendant of a quadrant on a given level.
+ * \param [in]  q      Input quadrant.
+ * \param [out] ld     Last descendant of \a q on level \a level.
+ * \param [in]  level  Level must be greater equal than q's level.
+ */
+void                p8est_quadrant_last_descendant (const p8est_quadrant_t *
+                                                    q, p8est_quadrant_t * ld,
+                                                    int level);
+
+/** Compute the descendant of a quadrant touching a given corner.
+ * \param [in]     q   Input quadrant.
+ * \param [in,out] r   Existing quadrant whose Morton index will be filled.
+ *                     Its user_data will be untouched.
+ * \param [in]     c   The corner of \a q that \a r touches.
+ * \param [in] level   The size of \a r.
+ */
+void                p8est_quadrant_corner_descendant (const p8est_quadrant_t *
+                                                      q, p8est_quadrant_t * r,
+                                                      int c, int level);
+
+/** Computes the nearest common ancestor of two quadrants in the same tree.
+ * \param [in]     q1 First input quadrant.
+ * \param [in]     q2 Second input quadrant.
+ * \param [in,out] r Existing quadrant whose Morton index will be filled.
+ *                   Its user_data will be untouched.
+ * \note \a q1, \a q2, \a r may point to the same quadrant.
+ *       The user_data of \a r is never modified.
+ */
+void                p8est_nearest_common_ancestor (const p8est_quadrant_t *
+                                                   q1,
+                                                   const p8est_quadrant_t *
+                                                   q2, p8est_quadrant_t * r);
+
+/** Computes the nearest common ancestor of two quadrants in the same tree.
+ * Descriptive, slower version of \a p8est_nearest_common_ancestor.
+ * For debugging and educationial purposes only.
+ */
+void                p8est_nearest_common_ancestor_D (const p8est_quadrant_t *
+                                                     q1,
+                                                     const p8est_quadrant_t *
+                                                     q2,
+                                                     p8est_quadrant_t * r);
+
+/** Transforms a quadrant/node across a face between trees.
+ * \param [in]     q        Input quadrant/non-clamped node.
+ * \param [in,out] r        Quadrant/node whose Morton index will be filled.
+ * \param [in] ftransform   This array holds 9 integers.
+ *             [0]..[2]     The coordinate axis sequence of the origin face.
+ *             [3]..[5]     The coordinate axis sequence of the target face.
+ *             [6]..[8]     Edge reverse flag for axes 0, 1; face code for 2.
+ * \note \a q and \q r may NOT point to the same quadrant structure.
+ */
+void                p8est_quadrant_transform_face (const p8est_quadrant_t * q,
+                                                   p8est_quadrant_t * r,
+                                                   const int ftransform[]);
+
+/** Checks if a quadrant touches an edge (diagonally inside or outside).
+ */
+int                 p8est_quadrant_touches_edge (const p8est_quadrant_t * q,
+                                                 int edge, int inside);
+
+/** Transforms a quadrant across an edge between trees.
+ * \param [in]     q          Input quadrant.
+ * \param [in,out] r          Quadrant whose Morton index will be filled.
+ * \param [in]     edge       Edge index of the originating quadrant.
+ * \param [in]     ei         Edge information computed previously.
+ * \param [in]     inside     The quadrant will be placed inside or outside.
+ */
+void                p8est_quadrant_transform_edge (const p8est_quadrant_t * q,
+                                                   p8est_quadrant_t * r,
+                                                   const p8est_edge_info_t *
+                                                   ei,
+                                                   const
+                                                   p8est_edge_transform_t *
+                                                   et, int inside);
+
+/** Shifts a quadrant until it touches the specified edge from the inside.
+ * If this shift is meant to recreate the effects of \a q on balancing across
+ * the edge, then \a r, \a rup, and \a rdown may all be necessary for that
+ * recreation.
+ * \param [in]     q          Valid input quadrant.
+ * \param [out]    r          Quadrant whose Morton index will be filled.
+ *                            This quadrant results from shifting \a q
+ *                            laterally towards the edge.
+ * \param [out]    rup        Quadrant whose Morton index will be filled (may
+ *                            be NULL).  This quadrant results from shifting
+ *                            \a q diagonally towards \a edge's higher
+ *                            corner.
+ * \param [out]    rdown      Quadrant whose Morton index will be filled (may
+ *                            be NULL).  This quadrant results from shifting
+ *                            \a q diagonally towards \a edge's lower corner.
+ * \param [in]     edge       Edge index.
+ */
+void                p8est_quadrant_shift_edge (const p8est_quadrant_t * q,
+                                               p8est_quadrant_t * r,
+                                               p8est_quadrant_t * rup,
+                                               p8est_quadrant_t * rdown,
+                                               int edge);
+
+/** Checks if a quadrant touches a corner (diagonally inside or outside).
+ */
+int                 p8est_quadrant_touches_corner (const p8est_quadrant_t * q,
+                                                   int corner, int inside);
+
+/** Move a quadrant inside or diagonally outside a corner position.
+ * \param [in,out] q        This quadrant only requires a valid level.
+ * \param [in]     icorner  Number of the corner in 0..7.
+ * \param [int]    inside   Boolean flag for inside or diagonally outside.
+ */
+void                p8est_quadrant_transform_corner (p8est_quadrant_t * r,
+                                                     int corner, int inside);
+
+/** Shifts a quadrant until it touches the specified corner from the inside.
+ * \param [in]     q          Valid input quadrant.
+ * \param [in,out] r          Quadrant whose Morton index will be filled.
+ * \param [in]     corner     Corner index.
+ */
+void                p8est_quadrant_shift_corner (const p8est_quadrant_t * q,
+                                                 p8est_quadrant_t * r,
+                                                 int corner);
+
+/** Computes the linear position of a quadrant in a uniform grid.
+ * \param [in] quadrant  Quadrant whose id will be computed.
+ * \return Returns the linear position of this quadrant on a grid.
+ * \note This is the inverse operation of p8est_quadrant_set_morton.
+ *       The user_data of \a quadrant is never modified.
+ */
+uint64_t            p8est_quadrant_linear_id (const p8est_quadrant_t *
+                                              quadrant, int level);
+
+/** Set quadrant Morton indices based on linear position in uniform grid.
+ * \param [in,out] quadrant  Quadrant whose Morton indices will be set.
+ * \param [in]     id        The linear position of this quadrant on a grid.
+ * \note This is the inverse operation of p8est_quadrant_linear_id.
+ *       The user_data of \a quadrant is never modified.
+ */
+void                p8est_quadrant_set_morton (p8est_quadrant_t * quadrant,
+                                               int level, uint64_t id);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_BITS_H */
diff --git a/src/p8est_communication.c b/src/p8est_communication.c
new file mode 100644
index 0000000..7522aa1
--- /dev/null
+++ b/src/p8est_communication.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_communication.c"
diff --git a/src/p8est_communication.h b/src/p8est_communication.h
new file mode 100644
index 0000000..6905d46
--- /dev/null
+++ b/src/p8est_communication.h
@@ -0,0 +1,159 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P8EST_COMMUNICATION_H
+#define P8EST_COMMUNICATION_H
+
+#include <p8est.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef enum
+{
+  P8EST_COMM_COUNT_PERTREE = 1,
+  P8EST_COMM_BALANCE_FIRST_COUNT,
+  P8EST_COMM_BALANCE_FIRST_LOAD,
+  P8EST_COMM_BALANCE_SECOND_COUNT,
+  P8EST_COMM_BALANCE_SECOND_LOAD,
+  P8EST_COMM_PARTITION_GIVEN,
+  P8EST_COMM_PARTITION_WEIGHTED_LOW,
+  P8EST_COMM_PARTITION_WEIGHTED_HIGH,
+  P8EST_COMM_PARTITION_CORRECTION,
+  P8EST_COMM_GHOST_COUNT,
+  P8EST_COMM_GHOST_LOAD,
+  P8EST_COMM_GHOST_EXCHANGE,
+  P8EST_COMM_GHOST_EXPAND_COUNT,
+  P8EST_COMM_GHOST_EXPAND_LOAD,
+  P8EST_COMM_GHOST_SUPPORT_COUNT,
+  P8EST_COMM_GHOST_SUPPORT_LOAD,
+  P8EST_COMM_GHOST_CHECKSUM,
+  P8EST_COMM_NODES_QUERY,
+  P8EST_COMM_NODES_REPLY,
+  P8EST_COMM_SAVE,
+  P8EST_COMM_LNODES_TEST,
+  P8EST_COMM_LNODES_PASS,
+  P8EST_COMM_LNODES_OWNED,
+  P8EST_COMM_LNODES_ALL
+}
+p8est_comm_tag_t;
+
+/** Caculate the number and partition of quadrents.
+ * \param [in,out] p8est  Adds all \c p8est->local_num_quadrant counters and
+ *                        puts cumulative sums in p4est->global_first_quadrant.
+ */
+void                p8est_comm_count_quadrants (p8est_t * p8est);
+
+/** Distribute the global partition boundaries.
+ * \param [in,out] p8est        Fills \c p8est->global_first_position.
+ *                              p8est->first_local_tree must be set correctly.
+ *                              If this processor is not empty and
+ *                              first_quad is NULL, the first quadrant
+ *                              of the first local tree must be set correctly.
+ * \param [in] first_quad       If not NULL will be used as first quadrant.
+ */
+void                p8est_comm_global_partition (p8est_t * p8est,
+                                                 p8est_quadrant_t *
+                                                 first_quad);
+
+/** Compute and distribute the cumulative number of quadrants per tree.
+ * \param [in] p8est    This p8est needs to have correct values for
+ *                      global_first_quadrant and global_first_position.
+ * \param [in,out] pertree      On input, memory for num_trees + 1 numbers.
+ *                              On output, the cumulative quadrant counts.
+ */
+void                p8est_comm_count_pertree (p8est_t * p8est,
+                                              p4est_gloidx_t * pertree);
+
+/** Tests ownershop of a quadrant via p8est->global_first_position.
+ * Assumes a tree with no overlaps.
+ * \param [in] rank    Rank whose ownership is tested.
+ * \return true if rank is the owner.
+ */
+int                 p8est_comm_is_owner (p8est_t * p8est,
+                                         p4est_locidx_t which_tree,
+                                         const p8est_quadrant_t * q,
+                                         int rank);
+
+/** Searches the owner of a quadrant via p8est->global_first_position.
+ * Assumes a tree with no overlaps.
+ * \param [in] guess   Initial guess for the search.
+ * \return Returns the processor id of the owner.
+ */
+int                 p8est_comm_find_owner (p8est_t * p8est,
+                                           p4est_locidx_t which_tree,
+                                           const p8est_quadrant_t * q,
+                                           int guess);
+
+/** Computes information about a tree being fully owned.
+ * This is determined separately for the beginning and end of the tree.
+ * \param [in] p8est            The p8est to work on.
+ * \param [in] which_tree       The tree in question must be partially owned.
+ * \param [out] full_tree[2]    Full ownership of beginning and end of tree.
+ * \param [out] tree_contact[6] True if there are neighbors across the face.
+ * \param [out] firstq          Smallest possible first quadrant on this core.
+ * \param [out] nextq           Smallest possible first quadrant on next core.
+ *                          Any of tree_contact, firstq and nextq may be NULL.
+ */
+void                p8est_comm_tree_info (p8est_t * p8est,
+                                          p4est_locidx_t which_tree,
+                                          int full_tree[],
+                                          int tree_contact[],
+                                          const p8est_quadrant_t ** firstq,
+                                          const p8est_quadrant_t ** nextq);
+
+/** Test if the 3x3 neighborhood of a quadrant is owned by this processor.
+ * \param [in] p8est            The p8est to work on.
+ * \param [in] which_tree       The tree index to work on.
+ * \param [in] full_tree[2]     Flags as computed by p4est_comm_tree_info.
+ * \param [in] tree_contact[6]  Flags as computed by p4est_comm_tree_info.
+ * \param [in] q                The quadrant to be checked.
+ * \return          Returns true iff this quadrant's 3x3 neighborhood is owned.
+ */
+int                 p8est_comm_neighborhood_owned (p8est_t * p8est,
+                                                   p4est_locidx_t which_tree,
+                                                   int full_tree[],
+                                                   int tree_contact[],
+                                                   p8est_quadrant_t * q);
+
+/** Evaluates true/false of a flag among processors.
+ * \param [in] p8est        The MPI communicator of this p8est will be used.
+ * \param [in] flag         The variable to communicate.
+ * \param [in] operation    Either sc_MPI_BAND or sc_MPI_BOR (not used bitwise).
+ * \return          Returns the logical AND resp. OR of all processors' flags.
+ */
+int                 p8est_comm_sync_flag (p8est_t * p8est,
+                                          int flag, sc_MPI_Op operation);
+
+/** Compute a parallel checksum out of local checksums.
+ * \param [in] p8est       The MPI information of this p8est will be used.
+ * \param [in] local_crc   Locally computed adler32 checksum.
+ * \param [in] local_bytes Number of bytes used for local checksum.
+ * \return                 Parallel checksum on rank 0, 0 otherwise.
+ */
+unsigned            p8est_comm_checksum (p8est_t * p8est,
+                                         unsigned local_crc,
+                                         size_t local_bytes);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_COMMUNICATION_H */
diff --git a/src/p8est_connectivity.c b/src/p8est_connectivity.c
new file mode 100644
index 0000000..edb9342
--- /dev/null
+++ b/src/p8est_connectivity.c
@@ -0,0 +1,1061 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include <p8est_connectivity.h>
+#include <sc_io.h>
+
+/* *INDENT-OFF* */
+const int           p8est_face_corners[6][4] =
+{{ 0, 2, 4, 6 },
+ { 1, 3, 5, 7 },
+ { 0, 1, 4, 5 },
+ { 2, 3, 6, 7 },
+ { 0, 1, 2, 3 },
+ { 4, 5, 6, 7 }};
+const int           p8est_face_edges[6][4] =
+{{ 4, 6,  8, 10 },
+ { 5, 7,  9, 11 },
+ { 0, 2,  8,  9 },
+ { 1, 3, 10, 11 },
+ { 0, 1,  4,  5 },
+ { 2, 3,  6,  7 }};
+const int           p8est_face_dual[6] = { 1, 0, 3, 2, 5, 4 };
+const int           p8est_face_permutations[8][4] =
+{{ 0, 1, 2, 3 },                /* no.  0 of 0..23 */
+ { 0, 2, 1, 3 },                /* no.  2 of 0..23 */
+ { 1, 0, 3, 2 },                /* no.  7 of 0..23 */
+ { 1, 3, 0, 2 },                /* no. 10 of 0..23 */
+ { 2, 0, 3, 1 },                /* no. 13 of 0..23 */
+ { 2, 3, 0, 1 },                /* no. 16 of 0..23 */
+ { 3, 1, 2, 0 },                /* no. 21 of 0..23 */
+ { 3, 2, 1, 0 }};               /* no. 23 of 0..23 */
+const int           p8est_face_permutation_sets[3][4] =
+{{ 1, 2, 5, 6 },
+ { 0, 3, 4, 7 },
+ { 0, 4, 3, 7 }};
+const int           p8est_face_permutation_refs[6][6] =
+{{ 0, 1, 1, 0, 0, 1 },
+ { 2, 0, 0, 1, 1, 0 },
+ { 2, 0, 0, 1, 1, 0 },
+ { 0, 2, 2, 0, 0, 1 },
+ { 0, 2, 2, 0, 0, 1 },
+ { 2, 0, 0, 2, 2, 0 }};
+
+const int           p8est_edge_faces[12][2] =
+{{ 2, 4 },
+ { 3, 4 },
+ { 2, 5 },
+ { 3, 5 },
+ { 0, 4 },
+ { 1, 4 },
+ { 0, 5 },
+ { 1, 5 },
+ { 0, 2 },
+ { 1, 2 },
+ { 0, 3 },
+ { 1, 3 }};
+const int           p8est_edge_corners[12][2] =
+{{ 0, 1 },
+ { 2, 3 },
+ { 4, 5 },
+ { 6, 7 },
+ { 0, 2 },
+ { 1, 3 },
+ { 4, 6 },
+ { 5, 7 },
+ { 0, 4 },
+ { 1, 5 },
+ { 2, 6 },
+ { 3, 7 }};
+const int           p8est_edge_face_corners[12][6][2] =
+{{{ -1, -1 }, { -1, -1 }, {  0,  1 }, { -1, -1 }, {  0,  1 }, { -1, -1 }},
+ {{ -1, -1 }, { -1, -1 }, { -1, -1 }, {  0,  1 }, {  2,  3 }, { -1, -1 }},
+ {{ -1, -1 }, { -1, -1 }, {  2,  3 }, { -1, -1 }, { -1, -1 }, {  0,  1 }},
+ {{ -1, -1 }, { -1, -1 }, { -1, -1 }, {  2,  3 }, { -1, -1 }, {  2,  3 }},
+ {{  0,  1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, {  0,  2 }, { -1, -1 }},
+ {{ -1, -1 }, {  0,  1 }, { -1, -1 }, { -1, -1 }, {  1,  3 }, { -1, -1 }},
+ {{  2,  3 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, {  0,  2 }},
+ {{ -1, -1 }, {  2,  3 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, {  1,  3 }},
+ {{  0,  2 }, { -1, -1 }, {  0,  2 }, { -1, -1 }, { -1, -1 }, { -1, -1 }},
+ {{ -1, -1 }, {  0,  2 }, {  1,  3 }, { -1, -1 }, { -1, -1 }, { -1, -1 }},
+ {{  1,  3 }, { -1, -1 }, { -1, -1 }, {  0,  2 }, { -1, -1 }, { -1, -1 }},
+ {{ -1, -1 }, {  1,  3 }, { -1, -1 }, {  1,  3 }, { -1, -1 }, { -1, -1 }}};
+
+const int           p8est_corner_faces[8][3] =
+{{ 0, 2, 4 },
+ { 1, 2, 4 },
+ { 0, 3, 4 },
+ { 1, 3, 4 },
+ { 0, 2, 5 },
+ { 1, 2, 5 },
+ { 0, 3, 5 },
+ { 1, 3, 5 }};
+const int           p8est_corner_edges[8][3] =
+{{ 0, 4,  8 },
+ { 0, 5,  9 },
+ { 1, 4, 10 },
+ { 1, 5, 11 },
+ { 2, 6,  8 },
+ { 2, 7,  9 },
+ { 3, 6, 10 },
+ { 3, 7, 11 }};
+const int           p8est_corner_face_corners[8][6] =
+{{  0, -1,  0, -1,  0, -1 },
+ { -1,  0,  1, -1,  1, -1 },
+ {  1, -1, -1,  0,  2, -1 },
+ { -1,  1, -1,  1,  3, -1 },
+ {  2, -1,  2, -1, -1,  0 },
+ { -1,  2,  3, -1, -1,  1 },
+ {  3, -1, -1,  2, -1,  2 },
+ { -1,  3, -1,  3, -1,  3 }};
+
+const int           p8est_child_edge_faces[8][12] =
+{{ -1,  4,  2, -1, -1,  4,  0, -1, -1,  2,  0, -1 },
+ { -1,  4,  2, -1,  4, -1, -1,  1,  2, -1, -1,  1 },
+ {  4, -1, -1,  3, -1,  4,  0, -1,  0, -1, -1,  3 },
+ {  4, -1, -1,  3,  4, -1, -1,  1, -1,  1,  3, -1 },
+ {  2, -1, -1,  5,  0, -1, -1,  5, -1,  2,  0, -1 },
+ {  2, -1, -1,  5, -1,  1,  5, -1,  2, -1, -1,  1 },
+ { -1,  3,  5, -1,  0, -1, -1,  5,  0, -1, -1,  3 },
+ { -1,  3,  5, -1, -1,  1,  5, -1, -1,  1,  3, -1 }};
+const int           p8est_child_corner_faces[8][8] =
+{{ -1, -1, -1,  4, -1,  2,  0, -1 },
+ { -1, -1,  4, -1,  2, -1, -1,  1 },
+ { -1,  4, -1, -1,  0, -1, -1,  3 },
+ {  4, -1, -1, -1, -1,  1,  3, -1 },
+ { -1,  2,  0, -1, -1, -1, -1,  5 },
+ {  2, -1, -1,  1, -1, -1,  5, -1 },
+ {  0, -1, -1,  3, -1,  5, -1, -1 },
+ { -1,  1,  3, -1,  5, -1, -1, -1 }};
+const int           p8est_child_corner_edges[8][8] =
+{{ -1,  0,  4, -1,  8, -1, -1, -1 },
+ {  0, -1, -1,  5, -1,  9, -1, -1 },
+ {  4, -1, -1,  1, -1, -1, 10, -1 },
+ { -1,  5,  1, -1, -1, -1, -1, 11 },
+ {  8, -1, -1, -1, -1,  2,  6, -1 },
+ { -1,  9, -1, -1,  2, -1, -1,  7 },
+ { -1, -1, 10, -1,  6, -1, -1,  3 },
+ { -1, -1, -1, 11, -1,  7,  3, -1 }};
+/* *INDENT-ON* */
+
+int
+p8est_connectivity_face_neighbor_corner_set (int c, int f, int nf, int set)
+{
+  int                 fc, nfc;
+
+  P4EST_ASSERT (0 <= c && c < P4EST_CHILDREN);
+  P4EST_ASSERT (0 <= f && f < P4EST_FACES);
+  P4EST_ASSERT (0 <= nf && nf < P4EST_FACES);
+  P4EST_ASSERT (0 <= set && set < 8);
+
+  fc = p8est_corner_face_corners[c][f];
+  P4EST_ASSERT (0 <= fc && fc < P4EST_HALF);
+  nfc = p8est_face_permutations[set][fc];
+  P4EST_ASSERT (0 <= nfc && nfc < P4EST_HALF);
+
+  return p8est_face_corners[nf][nfc];
+}
+
+p4est_connectivity_t *
+p8est_connectivity_new_unitcube (void)
+{
+  const p4est_topidx_t num_vertices = 8;
+  const p4est_topidx_t num_trees = 1;
+  const p4est_topidx_t num_ett = 0;
+  const p4est_topidx_t num_ctt = 0;
+  const double        vertices[8 * 3] = {
+    0, 0, 0,
+    1, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+    0, 0, 1,
+    1, 0, 1,
+    0, 1, 1,
+    1, 1, 1,
+  };
+  const p4est_topidx_t tree_to_vertex[1 * 8] = {
+    0, 1, 2, 3, 4, 5, 6, 7,
+  };
+  const p4est_topidx_t tree_to_tree[1 * 6] = {
+    0, 0, 0, 0, 0, 0,
+  };
+  const int8_t        tree_to_face[1 * 6] = {
+    0, 1, 2, 3, 4, 5,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, 0, 0,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      NULL, &num_ett, NULL, NULL,
+                                      NULL, &num_ctt, NULL, NULL);
+}
+
+p4est_connectivity_t *
+p8est_connectivity_new_periodic (void)
+{
+  const p4est_topidx_t num_vertices = 8;
+  const p4est_topidx_t num_trees = 1;
+  const p4est_topidx_t num_edges = 3;
+  const p4est_topidx_t num_corners = 1;
+  const double        vertices[8 * 3] = {
+    0, 0, 0,
+    1, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+    0, 0, 1,
+    1, 0, 1,
+    0, 1, 1,
+    1, 1, 1,
+  };
+  const p4est_topidx_t tree_to_vertex[1 * 8] = {
+    0, 1, 2, 3, 4, 5, 6, 7,
+  };
+  const p4est_topidx_t tree_to_tree[1 * 6] = {
+    0, 0, 0, 0, 0, 0,
+  };
+  const int8_t        tree_to_face[1 * 6] = {
+    1, 0, 3, 2, 5, 4,
+  };
+  const p4est_topidx_t tree_to_edge[1 * 12] = {
+    0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+  };
+  const p4est_topidx_t ett_offset[3 + 1] = {
+    0, 4, 8, 12,
+  };
+  const p4est_topidx_t edge_to_tree[12] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  };
+  const int8_t        edge_to_edge[12] = {
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+  };
+  const p4est_topidx_t tree_to_corner[1 * 8] = {
+    0, 0, 0, 0, 0, 0, 0, 0,
+  };
+  const p4est_topidx_t ctt_offset[1 + 1] = {
+    0, 8,
+  };
+  const p4est_topidx_t corner_to_tree[8] = {
+    0, 0, 0, 0, 0, 0, 0, 0,
+  };
+  const int8_t        corner_to_corner[8] = {
+    0, 1, 2, 3, 4, 5, 6, 7,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees,
+                                      num_edges, num_corners,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      tree_to_edge, ett_offset,
+                                      edge_to_tree, edge_to_edge,
+                                      tree_to_corner, ctt_offset,
+                                      corner_to_tree, corner_to_corner);
+}
+
+p4est_connectivity_t *
+p8est_connectivity_new_rotwrap (void)
+{
+  const p4est_topidx_t num_vertices = 8;
+  const p4est_topidx_t num_trees = 1;
+  const p4est_topidx_t num_edges = 4;
+  const p4est_topidx_t num_corners = 1;
+  const double        vertices[8 * 3] = {
+    0, 0, 0,
+    1, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+    0, 0, 1,
+    1, 0, 1,
+    0, 1, 1,
+    1, 1, 1,
+  };
+  const p4est_topidx_t tree_to_vertex[1 * 8] = {
+    0, 1, 2, 3, 4, 5, 6, 7,
+  };
+  const p4est_topidx_t tree_to_tree[1 * 6] = {
+    0, 0, 0, 0, 0, 0,
+  };
+  const int8_t        tree_to_face[1 * 6] = {
+    1, 0, 2, 3, 11, 10,
+  };
+  const p4est_topidx_t tree_to_edge[1 * 12] = {
+    0, 0, 1, 1, 1, 1, 0, 0, 2, 2, 3, 3,
+  };
+  const p4est_topidx_t ett_offset[4 + 1] = {
+    0, 4, 8, 10, 12,
+  };
+  const p4est_topidx_t edge_to_tree[12] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  };
+  const int8_t        edge_to_edge[12] = {
+    0, 7, 1, 6, 2, 16, 3, 17, 8, 9, 10, 11,
+  };
+  const p4est_topidx_t tree_to_corner[1 * 8] = {
+    0, 0, 0, 0, 0, 0, 0, 0,
+  };
+  const p4est_topidx_t ctt_offset[1 + 1] = {
+    0, 8,
+  };
+  const p4est_topidx_t corner_to_tree[8] = {
+    0, 0, 0, 0, 0, 0, 0, 0,
+  };
+  const int8_t        corner_to_corner[8] = {
+    0, 1, 2, 3, 4, 5, 6, 7,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees,
+                                      num_edges, num_corners,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      tree_to_edge, ett_offset,
+                                      edge_to_tree, edge_to_edge,
+                                      tree_to_corner, ctt_offset,
+                                      corner_to_tree, corner_to_corner);
+}
+
+p4est_connectivity_t *
+p8est_connectivity_new_twocubes (void)
+{
+  const p4est_topidx_t num_vertices = 12;
+  const p4est_topidx_t num_trees = 2;
+  const p4est_topidx_t num_ett = 0;
+  const p4est_topidx_t num_ctt = 0;
+  const double        vertices[12 * 3] = {
+    0, 0, 0,
+    1, 0, 0,
+    2, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+    2, 1, 0,
+    0, 0, 1,
+    1, 0, 1,
+    2, 0, 1,
+    0, 1, 1,
+    1, 1, 1,
+    2, 1, 1,
+  };
+  const p4est_topidx_t tree_to_vertex[2 * 8] = {
+    0, 1, 3, 4, 6, 7, 9, 10,
+    1, 2, 4, 5, 7, 8, 10, 11,
+  };
+  const p4est_topidx_t tree_to_tree[2 * 6] = {
+    0, 1, 0, 0, 0, 0,
+    0, 1, 1, 1, 1, 1,
+  };
+  const int8_t        tree_to_face[2 * 6] = {
+    0, 0, 2, 3, 4, 5,
+    1, 1, 2, 3, 4, 5,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, 0, 0,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      NULL, &num_ett, NULL, NULL,
+                                      NULL, &num_ctt, NULL, NULL);
+}
+
+p4est_connectivity_t *
+p8est_connectivity_new_twowrap (void)
+{
+  const p4est_topidx_t num_vertices = 12;
+  const p4est_topidx_t num_trees = 2;
+  const p4est_topidx_t num_ett = 0;
+  const p4est_topidx_t num_ctt = 0;
+  const double        vertices[12 * 3] = {
+    0, 0, 0,
+    1, 0, 0,
+    2, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+    2, 1, 0,
+    0, 0, 1,
+    1, 0, 1,
+    2, 0, 1,
+    0, 1, 1,
+    1, 1, 1,
+    2, 1, 1,
+  };
+  const p4est_topidx_t tree_to_vertex[2 * 8] = {
+    3, 9, 0, 6, 4, 10, 1, 7,
+    8, 2, 7, 1, 11, 5, 10, 4,
+  };
+  const p4est_topidx_t tree_to_tree[2 * 6] = {
+    0, 0, 0, 0, 1, 1,
+    1, 1, 0, 0, 1, 1,
+  };
+  const int8_t        tree_to_face[2 * 6] = {
+    0, 1, 2, 3, 20, 21,
+    0, 1, 22, 23, 4, 5,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, 0, 0,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      NULL, &num_ett, NULL, NULL,
+                                      NULL, &num_ctt, NULL, NULL);
+}
+
+p4est_connectivity_t *
+p8est_connectivity_new_rotcubes (void)
+{
+  const p4est_topidx_t num_vertices = 26;
+  const p4est_topidx_t num_trees = 6;
+  const p4est_topidx_t num_edges = 3;
+  const p4est_topidx_t num_corners = 1;
+  const double        vertices[26 * 3] = {
+    0, 0, 0,
+    1, 0, 2,
+    2, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+    2, 1, 0,
+    1, -1, 0,
+    2, -1, 0,
+    1, -1, 1,
+    2, -1, 1,
+    2, 1, 1,
+    1, 0, 1,
+    2, 0, 1,
+    0, 1, 1,
+    1, 1, 1,
+    0, 0, 1,
+    0, 0, 2,
+    1, 0, 0,
+    1, 1, 2,
+    0, 1, 2,
+    2.5, 1.5, 2,
+    2, 1.5, 2,
+    2, 1.5, 2.5,
+    2, .5, 2.5,
+    2.5, .5, 2,
+    2, .5, 2,
+  };
+  const p4est_topidx_t tree_to_vertex[6 * 8] = {
+    0, 17, 3, 4, 15, 11, 13, 14,
+    7, 2, 6, 17, 9, 12, 8, 11,
+    2, 12, 5, 10, 17, 11, 4, 14,
+    19, 13, 18, 14, 16, 15, 1, 11,
+    14, 11, 21, 25, 18, 1, 22, 23,
+    21, 20, 25, 24, 14, 10, 11, 12,
+  };
+  const p4est_topidx_t tree_to_tree[6 * 6] = {
+    0, 2, 0, 0, 0, 3,
+    1, 2, 1, 1, 1, 1,
+    2, 5, 1, 2, 2, 0,
+    3, 0, 3, 4, 3, 3,
+    4, 4, 3, 4, 5, 4,
+    4, 5, 5, 5, 5, 2,
+  };
+  const int8_t        tree_to_face[6 * 6] = {
+    0, 5, 2, 3, 4, 13,
+    0, 2, 2, 3, 4, 5,
+    0, 23, 1, 3, 4, 1,
+    0, 17, 2, 8, 4, 5,
+    0, 1, 9, 3, 12, 5,
+    16, 1, 2, 3, 4, 19,
+  };
+  const p4est_topidx_t tree_to_edge[6 * 12] = {
+    -1, -1, -1, -1, -1, -1, -1, 0, -1, 2, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 2,
+    -1, -1, 2, -1, -1, -1, -1, 0, -1, 1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,
+    0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, 1, -1, -1, 0, -1, -1, -1, -1, -1,
+  };
+  const p4est_topidx_t ett_offset[3 + 1] = { 0, 5, 8, 11 };
+  const p4est_topidx_t edge_to_tree[11] = {
+    0, 2, 3, 4, 5, 1, 2, 5, 0, 1, 2
+  };
+  const int8_t        edge_to_edge[11] = {
+    7, 7, 23, 12, 18, 7, 9, 15, 9, 11, 2
+  };
+  const p4est_topidx_t tree_to_corner[6 * 8] = {
+    -1, -1, -1, -1, -1, 0, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, 0,
+    -1, -1, -1, -1, -1, 0, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, 0,
+    -1, 0, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, 0, -1,
+  };
+  const p4est_topidx_t ctt_offset[1 + 1] = { 0, 6 };
+  const p4est_topidx_t corner_to_tree[6] = { 0, 1, 2, 3, 4, 5 };
+  const int8_t        corner_to_corner[6] = { 5, 7, 5, 7, 1, 6 };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees,
+                                      num_edges, num_corners,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      tree_to_edge, ett_offset,
+                                      edge_to_tree, edge_to_edge,
+                                      tree_to_corner, ctt_offset,
+                                      corner_to_tree, corner_to_corner);
+}
+
+p4est_connectivity_t *
+p8est_connectivity_new_shell (void)
+{
+/* *INDENT-OFF* */
+  const p4est_topidx_t num_vertices = 18;
+  const p4est_topidx_t num_trees =    24;
+  const p4est_topidx_t num_edges =    18;
+  const p4est_topidx_t num_corners =   0;
+  const p4est_topidx_t ctt_offset =    0;
+  const double        vertices[18 * 3] = {
+    -1, -1,  1,
+     0, -1,  1,
+     1, -1,  1,
+    -1,  0,  1,
+     0,  0,  1,
+     1,  0,  1,
+    -1,  1,  1,
+     0,  1,  1,
+     1,  1,  1,
+    -1, -1,  2,
+     0, -1,  2,
+     1, -1,  2,
+    -1,  0,  2,
+     0,  0,  2,
+     1,  0,  2,
+    -1,  1,  2,
+     0,  1,  2,
+     1,  1,  2,
+  };
+  const p4est_topidx_t tree_to_vertex[24 * 8] = {
+    0, 1, 3, 4,  9, 10, 12, 13,
+    1, 2, 4, 5, 10, 11, 13, 14,
+    3, 4, 6, 7, 12, 13, 15, 16,
+    4, 5, 7, 8, 13, 14, 16, 17,
+    0, 1, 3, 4,  9, 10, 12, 13,
+    1, 2, 4, 5, 10, 11, 13, 14,
+    3, 4, 6, 7, 12, 13, 15, 16,
+    4, 5, 7, 8, 13, 14, 16, 17,
+    0, 1, 3, 4,  9, 10, 12, 13,
+    1, 2, 4, 5, 10, 11, 13, 14,
+    3, 4, 6, 7, 12, 13, 15, 16,
+    4, 5, 7, 8, 13, 14, 16, 17,
+    0, 1, 3, 4,  9, 10, 12, 13,
+    1, 2, 4, 5, 10, 11, 13, 14,
+    3, 4, 6, 7, 12, 13, 15, 16,
+    4, 5, 7, 8, 13, 14, 16, 17,
+    0, 1, 3, 4,  9, 10, 12, 13,
+    1, 2, 4, 5, 10, 11, 13, 14,
+    3, 4, 6, 7, 12, 13, 15, 16,
+    4, 5, 7, 8, 13, 14, 16, 17,
+    0, 1, 3, 4,  9, 10, 12, 13,
+    1, 2, 4, 5, 10, 11, 13, 14,
+    3, 4, 6, 7, 12, 13, 15, 16,
+    4, 5, 7, 8, 13, 14, 16, 17,
+  };
+  const p4est_topidx_t tree_to_tree[24 * 6] = {
+    18,  1, 14,  2,  0,  0,
+     0, 23, 15,  3,  1,  1,
+    16,  3,  0,  4,  2,  2,
+     2, 21,  1,  5,  3,  3,
+    16,  5,  2,  6,  4,  4,
+     4, 21,  3,  7,  5,  5,
+    17,  7,  4,  8,  6,  6,
+     6, 20,  5,  9,  7,  7,
+    17,  9,  6, 10,  8,  8,
+     8, 20,  7, 11,  9,  9,
+    19, 11,  8, 12, 10, 10,
+    10, 22,  9, 13, 11, 11,
+    19, 13, 10, 14, 12, 12,
+    12, 22, 11, 15, 13, 13,
+    18, 15, 12,  0, 14, 14,
+    14, 23, 13,  1, 15, 15,
+     2, 17,  4, 18, 16, 16,
+    16,  8,  6, 19, 17, 17,
+     0, 19, 16, 14, 18, 18,
+    18, 10, 17, 12, 19, 19,
+     9, 21,  7, 22, 20, 20,
+    20,  3,  5, 23, 21, 21,
+    11, 23, 20, 13, 22, 22,
+    22,  1, 21, 15, 23, 23,
+  };
+  const int8_t        tree_to_face[24 * 6] = {
+    6, 0, 3, 2, 4, 5,
+    1, 7, 3, 2, 4, 5,
+    6, 0, 3, 2, 4, 5,
+    1, 7, 3, 2, 4, 5,
+    2, 0, 3, 2, 4, 5,
+    1, 8, 3, 2, 4, 5,
+    2, 0, 3, 2, 4, 5,
+    1, 8, 3, 2, 4, 5,
+    1, 0, 3, 2, 4, 5,
+    1, 0, 3, 2, 4, 5,
+    1, 0, 3, 2, 4, 5,
+    1, 0, 3, 2, 4, 5,
+    9, 0, 3, 2, 4, 5,
+    1, 3, 3, 2, 4, 5,
+    9, 0, 3, 2, 4, 5,
+    1, 3, 3, 2, 4, 5,
+    6, 0, 0, 2, 4, 5,
+    1, 0, 0, 2, 4, 5,
+    6, 0, 3, 6, 4, 5,
+    1, 0, 3, 6, 4, 5,
+    1, 0, 7, 2, 4, 5,
+    1, 7, 7, 2, 4, 5,
+    1, 0, 3, 1, 4, 5,
+    1, 7, 3, 1, 4, 5,
+  };
+  const p4est_topidx_t tree_to_edge[24 * 12] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1,  8,  6,  0,
+    -1, -1, -1, -1, -1, -1, -1, -1,  8, -1,  0,  7,
+    -1, -1, -1, -1, -1, -1, -1, -1,  6,  0, -1,  9,
+    -1, -1, -1, -1, -1, -1, -1, -1,  0,  7,  9, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1,  9, 10,  1,
+    -1, -1, -1, -1, -1, -1, -1, -1,  9, -1,  1, 11,
+    -1, -1, -1, -1, -1, -1, -1, -1, 10,  1, -1, 12,
+    -1, -1, -1, -1, -1, -1, -1, -1,  1, 11, 12, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 13,  2,
+    -1, -1, -1, -1, -1, -1, -1, -1, 12, -1,  2, 14,
+    -1, -1, -1, -1, -1, -1, -1, -1, 13,  2, -1, 15,
+    -1, -1, -1, -1, -1, -1, -1, -1,  2, 14, 15, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 16,  3,
+    -1, -1, -1, -1, -1, -1, -1, -1, 15, -1,  3, 17,
+    -1, -1, -1, -1, -1, -1, -1, -1, 16,  3, -1,  8,
+    -1, -1, -1, -1, -1, -1, -1, -1,  3, 17,  8, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, 10,  6,  4,
+    -1, -1, -1, -1, -1, -1, -1, -1, 10, -1,  4, 13,
+    -1, -1, -1, -1, -1, -1, -1, -1,  6,  4, -1, 16,
+    -1, -1, -1, -1, -1, -1, -1, -1,  4, 13, 16, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, 14,  5,
+    -1, -1, -1, -1, -1, -1, -1, -1, 11, -1,  5 , 7,
+    -1, -1, -1, -1, -1, -1, -1, -1, 14,  5, -1, 17,
+    -1, -1, -1, -1, -1, -1, -1, -1,  5,  7, 17, -1,
+  };
+  const p4est_topidx_t ett_offset[18 + 1] = {
+     0,  4 , 8, 12, 16, 20, 24, 28, 32,
+    36, 40, 44, 48, 52, 56, 60, 64, 68, 72,
+  };
+  const p4est_topidx_t edge_to_tree[72] = {
+     0,  1,  2,  3,
+     4,  5,  6,  7,
+     8,  9, 10, 11,
+    12, 13, 14, 15,
+    16, 17, 18, 19,
+    20, 21, 22, 23,
+     0,  2, 16, 18,
+     1,  3, 21, 23,
+     0,  1, 14, 15,
+     2,  3,  4,  5,
+     4,  6, 16, 17,
+     5,  7, 20, 21,
+     6,  7,  8,  9,
+     8, 10, 17, 19,
+     9, 11, 20, 22,
+    10, 11, 12, 13,
+    12, 14, 18, 19,
+    13, 15, 22, 23,
+  };
+  const int8_t        edge_to_edge[72] = {
+    11, 10,  9,  8,
+    11, 10,  9,  8,
+    11, 10,  9,  8,
+    11, 10,  9,  8,
+    11, 10,  9,  8,
+    11, 10,  9,  8,
+    10,  8, 10,  8,
+    11,  9, 11,  9,
+     9,  8, 11, 10,
+    11, 10,  9,  8,
+    10,  8,  9,  8,
+    11,  9,  9,  8,
+    11, 10,  9,  8,
+    10,  8, 11,  9,
+    11,  9, 10,  8,
+    11, 10,  9,  8,
+    10,  8, 11, 10,
+    11,  9, 11, 10,
+  };
+/* *INDENT-ON* */
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees,
+                                      num_edges, num_corners,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      tree_to_edge, ett_offset,
+                                      edge_to_tree, edge_to_edge,
+                                      NULL, &ctt_offset, NULL, NULL);
+}
+
+p4est_connectivity_t *
+p8est_connectivity_new_sphere (void)
+{
+/* *INDENT-OFF* */
+  const p4est_topidx_t num_vertices = 16;
+  const p4est_topidx_t num_trees =    13;
+  const p4est_topidx_t num_edges =    12;
+  const p4est_topidx_t num_corners =   0;
+  const p4est_topidx_t ctt_offset = 0;
+  const double        vertices[16 * 3] = {
+    -1, -1,  1,
+     1, -1,  1,
+    -1,  1,  1,
+     1,  1,  1,
+    -1, -1,  2,
+     1, -1,  2,
+    -1,  1,  2,
+     1,  1,  2,
+    -1, -1, -1,
+     1, -1, -1,
+    -1,  1, -1,
+     1,  1, -1,
+    -1, -1,  1,
+     1, -1,  1,
+    -1,  1,  1,
+     1,  1,  1,
+  };
+  const p4est_topidx_t tree_to_vertex[13 * 8] = {
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    0,  1,  2,  3,  4,  5,  6,  7,
+    8,  9, 10, 11, 12, 13, 14, 15,
+  };
+  const p4est_topidx_t tree_to_tree[13 * 6] = {
+     5,  3,  4,  1,  6,  0,
+     5,  3,  0,  2,  7,  1,
+     5,  3,  1,  4,  8,  2,
+     2,  0,  1,  4,  9,  3,
+     2,  0,  3,  5, 10,  4,
+     2,  0,  4,  1, 11,  5,
+    11,  9, 10,  7, 12,  0,
+    11,  9,  6,  8, 12,  1,
+    11,  9,  7, 10, 12,  2,
+     8,  6,  7, 10, 12,  3,
+     8,  6,  9, 11, 12,  4,
+     8,  6, 10,  7, 12,  5,
+    11,  9,  6,  8, 10,  7,
+  };
+  const int8_t        tree_to_face[13 * 6] = {
+     1,  7,  7,  2,  5,  5,
+     9,  8,  3,  2,  5,  5,
+     6,  0,  3,  6,  5,  5,
+     1,  7,  7,  2,  5,  5,
+     9,  8,  3,  2,  5,  5,
+     6,  0,  3,  6,  5,  5,
+     1,  7,  7,  2,  2,  4,
+     9,  8,  3,  2,  5,  4,
+     6,  0,  3,  6, 15,  4,
+     1,  7,  7,  2, 19,  4,
+     9,  8,  3,  2, 22,  4,
+     6,  0,  3,  6,  6,  4,
+    10, 22,  4, 16, 22,  4,
+  };
+  const p4est_topidx_t tree_to_edge[13 * 12] = {
+     0,  2, -1, -1,  8,  9, -1, -1, -1, -1, -1, -1,
+     2,  3, -1, -1,  6,  7, -1, -1, -1, -1, -1, -1,
+     3,  1, -1, -1, 10, 11, -1, -1, -1, -1, -1, -1,
+     7,  5, -1, -1, 11,  9, -1, -1, -1, -1, -1, -1,
+     5,  4, -1, -1,  1,  0, -1, -1, -1, -1, -1, -1,
+     4,  6, -1, -1, 10,  8, -1, -1, -1, -1, -1, -1,
+    -1, -1,  0,  2, -1, -1,  8,  9, -1, -1, -1, -1,
+    -1, -1,  2,  3, -1, -1,  6,  7, -1, -1, -1, -1,
+    -1, -1,  3,  1, -1, -1, 10, 11, -1, -1, -1, -1,
+    -1, -1,  7,  5, -1, -1, 11,  9, -1, -1, -1, -1,
+    -1, -1,  5,  4, -1, -1,  1,  0, -1, -1, -1, -1,
+    -1, -1,  4,  6, -1, -1, 10,  8, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  };
+  const p4est_topidx_t ett_offset[12 + 1] = {
+    0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48,
+  };
+  const p4est_topidx_t edge_to_tree[48] = {
+    0,  4,  6, 10,
+    2,  4,  8, 10,
+    0,  1,  6,  7,
+    1,  2,  7,  8,
+    4,  5, 10, 11,
+    3,  4,  9, 10,
+    1,  5,  7, 11,
+    1,  3,  7,  9,
+    0,  5,  6, 11,
+    0,  3,  6,  9,
+    2,  5,  8, 11,
+    2,  3,  8,  9,
+  };
+  const int8_t        edge_to_edge[48] = {
+     0, 17,  2, 19,
+     1, 16,  3, 18,
+     1,  0,  3,  2,
+     1,  0,  3,  2,
+    13, 12, 15, 14,
+    13, 12, 15, 14,
+     4, 13,  6, 15,
+     5, 12,  7, 14,
+     4,  5,  6,  7,
+     5, 17,  7, 19,
+    16,  4, 18,  6,
+    17, 16, 19, 18,
+  };
+#if 0   /* corner information would be redundant */
+  const p4est_topidx_t tree_to_corner[13 * 8] = {
+     0,  1,  4,  5, -1, -1, -1, -1,
+     4,  5,  6,  7, -1, -1, -1, -1,
+     6,  7,  2,  3, -1, -1, -1, -1,
+     7,  5,  3,  1, -1, -1, -1, -1,
+     3,  1,  2,  0, -1, -1, -1, -1,
+     2,  0,  6,  4, -1, -1, -1, -1,
+    -1, -1, -1, -1,  0,  1,  4,  5,
+    -1, -1, -1, -1,  4,  5,  6,  7,
+    -1, -1, -1, -1,  6,  7,  2,  3,
+    -1, -1, -1, -1,  7,  5,  3,  1,
+    -1, -1, -1, -1,  3,  1,  2,  0,
+    -1, -1, -1, -1,  2,  0,  6,  4,
+    -1, -1, -1, -1, -1, -1, -1, -1,
+  };
+  const p4est_topidx_t ctt_offset[8 + 1] = {
+    0, 6, 12, 18, 24, 30, 36, 42, 48,
+  };
+  const p4est_topidx_t corner_to_tree[48] = {
+    0,  4,  5,  6, 10, 11,
+    0,  3,  4,  6,  9, 10,
+    2,  4,  5,  8, 10, 11,
+    2,  3,  4,  8,  9, 10,
+    0,  1,  5,  6,  7, 11,
+    0,  1,  3,  6,  7,  9,
+    1,  2,  5,  7,  8, 11,
+    1,  2,  3,  7,  8,  9,
+  };
+  const int8_t        corner_to_corner[48] = {
+    0, 3, 1, 4, 7, 5,
+    1, 3, 1, 5, 7, 5,
+    2, 2, 0, 6, 6, 4,
+    3, 2, 0, 7, 6, 4,
+    2, 0, 3, 6, 4, 7,
+    3, 1, 1, 7, 5, 5,
+    2, 0, 2, 6, 4, 6,
+    3, 1, 0, 7, 5, 4,
+  };
+#endif  /* 0 */
+/* *INDENT-ON* */
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees,
+                                      num_edges, num_corners,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      tree_to_edge, ett_offset,
+                                      edge_to_tree, edge_to_edge,
+                                      NULL, &ctt_offset, NULL, NULL);
+}
+
+static int
+p8est_find_edge_transform_internal (p4est_connectivity_t * conn,
+                                    p4est_topidx_t itree, int iedge,
+                                    p8est_edge_info_t * ei,
+                                    const p4est_topidx_t * ett,
+                                    const int8_t * ete,
+                                    p4est_topidx_t edge_trees,
+                                    p4est_topidx_t ntrees[2])
+{
+  int                 i;
+  int                 redge, nedge, iflip, nflip;
+  int                 pref, pset, fc[2];
+  int                 faces[2], nfaces[2], orients[2];
+#ifdef P4EST_ENABLE_DEBUG
+  int                 founds[2];
+#endif
+  int                 nows[2];
+  p4est_topidx_t      etree, ietree, ntree;
+  p8est_edge_transform_t *et;
+  sc_array_t         *ta = &ei->edge_transforms;
+  const int           noncorners[2] = { -1, -1 };
+  const int          *fcorners[2] = { noncorners, noncorners };
+  const int          *nfcorners;
+  int                 flipped = 0;
+
+  P4EST_ASSERT (0 <= itree && itree < conn->num_trees);
+  P4EST_ASSERT (0 <= iedge && iedge < P8EST_EDGES);
+  P4EST_ASSERT (ta->elem_size == sizeof (p8est_edge_transform_t));
+
+  /* identify touching faces */
+  for (i = 0; i < 2; ++i) {
+    faces[i] = p8est_edge_faces[iedge][i];
+    ntrees[i] = conn->tree_to_tree[P4EST_FACES * itree + faces[i]];
+    nfaces[i] = (int) conn->tree_to_face[P4EST_FACES * itree + faces[i]];
+    if (ntrees[i] == itree && nfaces[i] == faces[i]) {  /* domain boundary */
+      ntrees[i] = -1;
+      nfaces[i] = orients[i] = -1;
+    }
+    else {
+      orients[i] = nfaces[i] / P4EST_FACES;
+      nfaces[i] %= P4EST_FACES;
+      fcorners[i] = p8est_edge_face_corners[iedge][faces[i]];
+      P4EST_ASSERT (fcorners[i][0] >= 0 && fcorners[i][1] >= 0);
+    }
+#ifdef P4EST_ENABLE_DEBUG
+    founds[i] = 0;
+#endif
+  }
+
+  /* find orientation of this edge */
+  ietree = -1;
+  iflip = -1;
+  for (etree = 0; etree < edge_trees; ++etree) {
+    ntree = ett[etree];
+    P4EST_ASSERT (0 <= ntree && ntree < conn->num_trees);
+    redge = (int) ete[etree];
+    P4EST_ASSERT (redge >= 0 && redge < 2 * P8EST_EDGES);
+    nedge = redge % P8EST_EDGES;
+    if (nedge == iedge && ntree == itree) {
+      iflip = redge / P8EST_EDGES;
+      ietree = etree;
+      break;
+    }
+  }
+  P4EST_ASSERT (ietree >= 0 && iflip >= 0);
+
+  /* loop through all trees connected through this edge */
+  for (etree = 0; etree < edge_trees; ++etree) {
+    if (etree == ietree) {
+      continue;
+    }
+    ntree = ett[etree];
+    P4EST_ASSERT (0 <= ntree && ntree < conn->num_trees);
+    redge = (int) ete[etree];
+    P4EST_ASSERT (redge >= 0 && redge < 2 * P8EST_EDGES);
+    nedge = redge % P8EST_EDGES;
+    nflip = (redge / P8EST_EDGES) ^ iflip;
+
+    nows[0] = nows[1] = 0;
+    for (i = 0; i < 2; ++i) {
+      if (ntree == ntrees[i]) {
+        /* check if the edge touches this neighbor contact face */
+        P4EST_ASSERT (fcorners[i][0] >= 0);
+        nfcorners = p8est_edge_face_corners[nedge][nfaces[i]];
+        if (nfcorners[0] >= 0) {
+          pref = p8est_face_permutation_refs[faces[i]][nfaces[i]];
+          pset = p8est_face_permutation_sets[pref][orients[i]];
+          fc[0] = p8est_face_permutations[pset][fcorners[i][0]];
+          fc[1] = p8est_face_permutations[pset][fcorners[i][1]];
+
+          if (fc[0] == nfcorners[nflip] && fc[1] == nfcorners[!nflip]) {
+            P4EST_ASSERT (!founds[i] && !nows[!i]);
+#ifdef P4EST_ENABLE_DEBUG
+            founds[i] = 1;
+#endif
+            nows[i] = 1;
+          }
+          else if (fc[0] == nfcorners[!nflip] && fc[1] == nfcorners[nflip]) {
+            ++flipped;
+          }
+        }
+      }
+    }
+    if (nows[0] || nows[1]) {
+      continue;
+    }
+
+    /* else we have a diagonal edge with ntree */
+    et = (p8est_edge_transform_t *) sc_array_push (ta);
+    et->ntree = ntree;
+    et->nedge = (int8_t) nedge;
+    et->naxis[0] = (int8_t) (nedge / 4);
+    et->naxis[1] = (int8_t) (nedge < 4 ? 1 : 0);
+    et->naxis[2] = (int8_t) (nedge < 8 ? 2 : 1);
+    et->nflip = (int8_t) nflip;
+    et->corners = (int8_t) (nedge % 4);
+  }
+
+  return flipped;
+}
+
+#include "p4est_connectivity.c"
+
+void
+p8est_find_edge_transform (p4est_connectivity_t * conn,
+                           p4est_topidx_t itree, int iedge,
+                           p8est_edge_info_t * ei)
+{
+  int                 flipped;
+  p4est_topidx_t      ntrees[2], edge_trees, aedge, ettae;
+  sc_array_t         *ta = &ei->edge_transforms;
+
+  P4EST_ASSERT (0 <= itree && itree < conn->num_trees);
+  P4EST_ASSERT (0 <= iedge && iedge < P8EST_EDGES);
+  P4EST_ASSERT (ta->elem_size == sizeof (p8est_edge_transform_t));
+
+  /* check if this edge exists at all */
+  ei->iedge = (int8_t) iedge;
+  sc_array_resize (ta, 0);
+  if (conn->num_edges == 0) {
+    return;
+  }
+  aedge = conn->tree_to_edge[P8EST_EDGES * itree + iedge];
+  if (aedge == -1) {
+    return;
+  }
+  P4EST_ASSERT (0 <= aedge && aedge < conn->num_edges);
+
+  /* retrieve connectivity information for this edge */
+  ettae = conn->ett_offset[aedge];
+  edge_trees = conn->ett_offset[aedge + 1] - ettae;
+  P4EST_ASSERT (0 <= ettae && 1 <= edge_trees);
+
+  /* loop through all edge neighbors and find edge connections */
+  flipped = p8est_find_edge_transform_internal (conn, itree, iedge, ei,
+                                                conn->edge_to_tree + ettae,
+                                                conn->edge_to_edge + ettae,
+                                                edge_trees, ntrees);
+  P4EST_ASSERT (edge_trees == (p4est_topidx_t) ta->elem_count
+                + 1 + (ntrees[0] != -1) + (ntrees[1] != -1) - flipped);
+}
+
+int
+p8est_connect_type_int (p8est_connect_type_t btype)
+{
+  switch (btype) {
+  case P8EST_CONNECT_FACE:
+    return 1;
+  case P8EST_CONNECT_EDGE:
+    return 2;
+  case P8EST_CONNECT_CORNER:
+    return 3;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+}
+
+const char         *
+p8est_connect_type_string (p8est_connect_type_t btype)
+{
+  switch (btype) {
+  case P8EST_CONNECT_FACE:
+    return "FACE";
+  case P8EST_CONNECT_EDGE:
+    return "EDGE";
+  case P8EST_CONNECT_CORNER:
+    return "CORNER";
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+}
diff --git a/src/p8est_connectivity.h b/src/p8est_connectivity.h
new file mode 100644
index 0000000..f258f1d
--- /dev/null
+++ b/src/p8est_connectivity.h
@@ -0,0 +1,877 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_connectivity.h
+ *
+ * The coarse topological description of the forest.
+ *
+ * \ingroup p8est
+ */
+
+#ifndef P8EST_CONNECTIVITY_H
+#define P8EST_CONNECTIVITY_H
+
+#include <sc_io.h>
+#include <p4est_base.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** The spatial dimension */
+#define P8EST_DIM 3
+/** The number of faces of an octant
+ *
+ * \note for uniform naming reasons, an
+ * octant is represented by the datatype p8est_quadrant_t */
+#define P8EST_FACES (2 * P8EST_DIM)
+/** The number of children of an octant
+ *
+ * also the nmber of corners */
+#define P8EST_CHILDREN 8
+/** The number of children/corners touching one face */
+#define P8EST_HALF (P8EST_CHILDREN / 2)
+/** The number of edges around an octant */
+#define P8EST_EDGES 12
+/** The size of insulation layer */
+#define P8EST_INSUL 27
+
+/* size of face transformation encoding */
+#define P8EST_FTRANSFORM 9
+
+/** p8est identification string */
+#define P8EST_STRING "p8est"
+
+/* Increase this number whenever the on-disk format for
+ * p8est_connectivity, p8est, or any other 3D data structure changes.
+ * The format for reading and writing must be the same.
+ */
+#define P8EST_ONDISK_FORMAT 0x3000009
+
+/** Characterize a type of adjacency.
+ *
+ * Several functions involve relationships between neighboring trees and/or
+ * quadrants, and their behavior depends on how one defines adjacency:
+ * 1) entities are adjacent if they share a face, or
+ * 2) entities are adjacent if they share a face or corner, or
+ * 3) entities are adjacent if they share a face, corner or edge.
+ * p8est_connect_type_t is used to choose the desired behavior.
+ * This enum must fit into an int8_t.
+ */
+typedef enum
+{
+  /* make sure to have different values 2D and 3D */
+  P8EST_CONNECT_FACE = 31,
+  P8EST_CONNECT_EDGE = 32,
+  P8EST_CONNECT_CORNER = 33,
+  P8EST_CONNECT_FULL = P8EST_CONNECT_CORNER
+}
+p8est_connect_type_t;
+
+#ifdef P4EST_BACKWARD_DEALII
+typedef p8est_connect_type_t p8est_balance_type_t;
+#endif
+
+/** Typedef for serialization method. */
+typedef enum
+{
+  P8EST_CONN_ENCODE_NONE = SC_IO_ENCODE_NONE,
+  P8EST_CONN_ENCODE_LAST        /**< Invalid entry to close the list. */
+}
+p8est_connectivity_encode_t;
+
+/** Convert the p8est_connect_type_t into a number.
+ * \param [in] btype    The balance type to convert.
+ * \return              Returns 1, 2 or 3.
+ */
+int                 p8est_connect_type_int (p8est_connect_type_t btype);
+
+/** Convert the p8est_connect_type_t into a const string.
+ * \param [in] btype    The balance type to convert.
+ * \return              Returns a pointer to a constant string.
+ */
+const char         *p8est_connect_type_string (p8est_connect_type_t btype);
+
+/** This structure holds the 3D inter-tree connectivity information.
+ * Identification of arbitrary faces, edges and corners is possible.
+ *
+ * The arrays tree_to_* are stored in z ordering.
+ * For corners the order wrt. zyx is 000 001 010 011 100 101 110 111.
+ * For faces the order is -x +x -y +y -z +z.
+ * They are allocated [0][0]..[0][N-1]..[num_trees-1][0]..[num_trees-1][N-1].
+ * where N is 6 for tree and face, 8 for corner, 12 for edge.
+ *
+ * The values for tree_to_face are in 0..23
+ * where ttf % 6 gives the face number and ttf / 6 the face orientation code.
+ * The orientation is determined as follows.  Let my_face and other_face
+ * be the two face numbers of the connecting trees in 0..5.  Then the first
+ * face corner of the lower of my_face and other_face connects to a face
+ * corner numbered 0..3 in the higher of my_face and other_face.  The face
+ * orientation is defined as this number.  If my_face == other_face, treating
+ * either of both faces as the lower one leads to the same result.
+ *
+ * It is valid to specify num_vertices as 0.
+ * In this case vertices and tree_to_vertex are set to NULL.
+ * Otherwise the vertex coordinates are stored in the array vertices as
+ * [0][0]..[0][2]..[num_vertices-1][0]..[num_vertices-1][2].
+ *
+ * The edges are only stored when they connect trees.
+ * Otherwise the tree_to_edge entry must be -1 and this edge is ignored.
+ * If num_edges == 0, tree_to_edge and edge_to_* arrays are set to NULL.
+ *
+ * The arrays edge_to_* store a variable number of entries per edge.
+ * For edge e these are at position [ett_offset[e]]..[ett_offset[e+1]-1].
+ * Their number for edge e is ett_offset[e+1] - ett_offset[e].
+ * The size of the edge_to_* arrays is num_ett = ett_offset[num_edges].
+ * The edge_to_edge array holds values in 0..23, where the lower 12 indicate
+ * one edge orientation and the higher 12 the opposite edge orientation.
+ *
+ * The corners are only stored when they connect trees.
+ * Otherwise the tree_to_corner entry must be -1 and this corner is ignored.
+ * If num_corners == 0, tree_to_corner and corner_to_* arrays are set to NULL.
+ *
+ * The arrays corner_to_* store a variable number of entries per corner.
+ * For corner c these are at position [ctt_offset[c]]..[ctt_offset[c+1]-1].
+ * Their number for corner c is ctt_offset[c+1] - ctt_offset[c].
+ * The size of the corner_to_* arrays is num_ctt = ctt_offset[num_corners].
+ *
+ * The *_to_attr arrays may have arbitrary contents defined by the user.
+ */
+typedef struct p8est_connectivity
+{
+  p4est_topidx_t      num_vertices; /**< the number of vertices that define
+                                         the \a embedding of the forest (not
+                                         the topology) */
+  p4est_topidx_t      num_trees;    /**< the number of trees */
+  p4est_topidx_t      num_edges;    /**< the number of edges that help define
+                                         the topology */
+  p4est_topidx_t      num_corners;  /**< the number of corners that help
+                                         define the topology */
+
+  double             *vertices;     /**< an array of size
+                                         (3 * \a num_vertices) */
+  p4est_topidx_t     *tree_to_vertex; /**< embed each tree into \f$R^3\f$ for
+                                           e.g. visualization (see
+                                           p8est_vtk.h) */
+
+  size_t              tree_attr_bytes;  /**< bytes per tree in tree_to_attr */
+  char               *tree_to_attr;     /**< not touched by p4est */
+
+  p4est_topidx_t     *tree_to_tree; /**< (6 * \a num_trees) neighbors across
+                                         faces */
+  int8_t             *tree_to_face; /**< (4 * \a num_trees) face to
+                                         face+orientation (see description) */
+  p4est_topidx_t     *tree_to_edge; /**< (12 * \a num_trees) or NULL (see
+                                          description) */
+  p4est_topidx_t     *ett_offset; /**< edge to offset in \a edge_to_tree and
+                                       \a edge_to_edge */
+  p4est_topidx_t     *edge_to_tree; /**< list of trees that meet at an edge */
+  int8_t             *edge_to_edge; /**< list of tree-edges+orientations that
+                                         meet at an edge (see description) */
+  p4est_topidx_t     *tree_to_corner; /**< (8 * \a num_trees) or NULL (see
+                                           description) */
+  p4est_topidx_t     *ctt_offset; /**< corner to offset in \a corner_to_tree
+                                       and \a corner_to_corner */
+  p4est_topidx_t     *corner_to_tree; /**< list of trees that meet at a corner */
+  int8_t             *corner_to_corner; /**< list of tree-corners that meet at
+                                             a corner */
+}
+p8est_connectivity_t;
+
+/** Calculate memory usage of a connectivity structure.
+ * \param [in] conn   Connectivity structure.
+ * \return            Memory used in bytes.
+ */
+size_t              p8est_connectivity_memory_used (p8est_connectivity_t *
+                                                    conn);
+
+typedef struct
+{
+  p4est_topidx_t      ntree;
+  int8_t              nedge, naxis[3], nflip, corners;
+}
+p8est_edge_transform_t;
+
+typedef struct
+{
+  int8_t              iedge;
+  sc_array_t          edge_transforms;
+}
+p8est_edge_info_t;
+
+typedef struct
+{
+  p4est_topidx_t      ntree;
+  int8_t              ncorner;
+}
+p8est_corner_transform_t;
+
+typedef struct
+{
+  p4est_topidx_t      icorner;
+  sc_array_t          corner_transforms;
+}
+p8est_corner_info_t;
+
+/** Store the corner numbers 0..7 for each tree face. */
+extern const int    p8est_face_corners[6][4];
+
+/** Store the face numbers 0..12 for each tree face. */
+extern const int    p8est_face_edges[6][4];
+
+/** Store the face numbers in the face neighbor's system. */
+extern const int    p8est_face_dual[6];
+
+/** Store only the 8 out of 24 possible permutations that occur. */
+extern const int    p8est_face_permutations[8][4];
+
+/** Store the 3 occurring sets of 4 permutations per face. */
+extern const int    p8est_face_permutation_sets[3][4];
+
+/** For each face combination store the permutation set.
+ * The order is [my_face][neighbor_face]
+ */
+extern const int    p8est_face_permutation_refs[6][6];
+
+/** Store the face numbers 0..5 for each tree edge. */
+extern const int    p8est_edge_faces[12][2];
+
+/** Store the corner numbers 0..8 for each tree edge. */
+extern const int    p8est_edge_corners[12][2];
+
+/** Store the face corner numbers for the faces touching a tree edge. */
+extern const int    p8est_edge_face_corners[12][6][2];
+
+/** Store the face numbers 0..5 for each tree corner. */
+extern const int    p8est_corner_faces[8][3];
+
+/** Store the edge numbers 0..11 for each tree corner. */
+extern const int    p8est_corner_edges[8][3];
+
+/** Store the face corner numbers for the faces touching a tree corner. */
+extern const int    p8est_corner_face_corners[8][6];
+
+/** Store the faces for each child and edge, can be -1. */
+extern const int    p8est_child_edge_faces[8][12];
+
+/** Store the faces for each child and corner, can be -1. */
+extern const int    p8est_child_corner_faces[8][8];
+
+/** Store the edges for each child and corner, can be -1. */
+extern const int    p8est_child_corner_edges[8][8];
+
+/** Transform a corner across one of the adjacent faces into a neighbor tree.
+ * It expects a face permutation index that has been precomputed.
+ * \param [in] c    A corner number in 0..7.
+ * \param [in] f    A face number that touches the corner \a c.
+ * \param [in] nf   A neighbor face that is on the other side of \f.
+ * \param [in] set  A value from \a p8est_face_permutation_sets that is
+ *                  obtained using \a f, \a nf, and a valid orientation:
+ *                  ref = p8est_face_permutation_refs[f][nf];
+ *                  set = p8est_face_permutation_sets[ref][orientation];
+ * \return          The corner number in 0..7 seen from the other face.
+ */
+int                 p8est_connectivity_face_neighbor_corner_set
+  (int c, int f, int nf, int set);
+
+/** Transform a corner across one of the adjacent faces into a neighbor tree.
+ * This version expects the neighbor face and orientation separately.
+ * \param [in] c    A corner number in 0..7.
+ * \param [in] f    A face number that touches the corner \a c.
+ * \param [in] nf   A neighbor face that is on the other side of \f.
+ * \param [in] o    The orientation between tree boundary faces \a f and \nf.
+ */
+int                 p8est_connectivity_face_neighbor_corner_orientation
+  (int c, int f, int nf, int o);
+
+/** Allocate a connectivity structure.
+ * The attribute fields are initialized to NULL.
+ * \param [in] num_vertices   Number of total vertices (i.e. geometric points).
+ * \param [in] num_trees      Number of trees in the forest.
+ * \param [in] num_edges      Number of tree-connecting edges.
+ * \param [in] num_ett        Number of total trees in edge_to_tree array.
+ * \param [in] num_corners    Number of tree-connecting corners.
+ * \param [in] num_ctt        Number of total trees in corner_to_tree array.
+ * \return                    A connectivity structure with allocated arrays.
+ */
+p8est_connectivity_t *p8est_connectivity_new (p4est_topidx_t num_vertices,
+                                              p4est_topidx_t num_trees,
+                                              p4est_topidx_t num_edges,
+                                              p4est_topidx_t num_ett,
+                                              p4est_topidx_t num_corners,
+                                              p4est_topidx_t num_ctt);
+
+/** Allocate a connectivity structure and populate from constants.
+ * The attribute fields are initialized to NULL.
+ * \param [in] num_vertices   Number of total vertices (i.e. geometric points).
+ * \param [in] num_trees      Number of trees in the forest.
+ * \param [in] num_edges      Number of tree-connecting edges.
+ * \param [in] num_corners    Number of tree-connecting corners.
+ * \param [in] eoff           Edge-to-tree offsets (num_edges + 1 values).
+ * \param [in] coff           Corner-to-tree offsets (num_corners + 1 values).
+ * \return                    The connectivity is checked for validity.
+ */
+p8est_connectivity_t *p8est_connectivity_new_copy (p4est_topidx_t
+                                                   num_vertices,
+                                                   p4est_topidx_t num_trees,
+                                                   p4est_topidx_t num_edges,
+                                                   p4est_topidx_t num_corners,
+                                                   const double *vertices,
+                                                   const p4est_topidx_t * ttv,
+                                                   const p4est_topidx_t * ttt,
+                                                   const int8_t * ttf,
+                                                   const p4est_topidx_t * tte,
+                                                   const p4est_topidx_t *
+                                                   eoff,
+                                                   const p4est_topidx_t * ett,
+                                                   const int8_t * ete,
+                                                   const p4est_topidx_t * ttc,
+                                                   const p4est_topidx_t *
+                                                   coff,
+                                                   const p4est_topidx_t * ctt,
+                                                   const int8_t * ctc);
+
+/** Destroy a connectivity structure.  Also destroy all attributes.
+ */
+void                p8est_connectivity_destroy (p8est_connectivity_t *
+                                                connectivity);
+
+/** Allocate or free the attribute fields in a connectivity.
+ * \param [in,out] conn         The conn->*_to_attr fields must either be NULL
+ *                              or previously be allocated by this function.
+ * \param [in] bytes_per_tree   If 0, tree_to_attr is freed (being NULL is ok).
+ *                              If positive, requested space is allocated.
+ */
+void                p8est_connectivity_set_attr (p8est_connectivity_t * conn,
+                                                 size_t bytes_per_tree);
+
+/** Examine a connectivity structure.
+ * \return  Returns true if structure is valid, false otherwise.
+ */
+int                 p8est_connectivity_is_valid (p8est_connectivity_t *
+                                                 connectivity);
+
+/** Check two connectivity structures for equality.
+ * \return          Returns true if structures are equal, false otherwise.
+ */
+int                 p8est_connectivity_is_equal (p8est_connectivity_t * conn1,
+                                                 p8est_connectivity_t *
+                                                 conn2);
+
+/** Write connectivity to a sink object.
+ * \param [in] conn     The connectivity to be written.
+ * \param [in,out] sink The connectivity is written into this sink.
+ * \return              0 on success, nonzero on error.
+ */
+int                 p8est_connectivity_sink (p8est_connectivity_t * conn,
+                                             sc_io_sink_t * sink);
+
+/** Allocate memory and store the connectivity information there.
+ * \param [in] conn     The connectivity structure to be exported to memory.
+ * \param [in] code     Encoding and compression method for serialization.
+ * \return              Newly created array that contains the information.
+ */
+sc_array_t         *p8est_connectivity_deflate (p8est_connectivity_t * conn,
+                                                p8est_connectivity_encode_t
+                                                code);
+
+/** Save a connectivity structure to disk.
+ * \param [in] filename         Name of the file to write.
+ * \param [in] connectivity     Valid connectivity structure.
+ * \return                      Returns 0 on success, nonzero on file error.
+ */
+int                 p8est_connectivity_save (const char *filename,
+                                             p8est_connectivity_t *
+                                             connectivity);
+
+/** Read connectivity from a source object.
+ * \param [in,out] source       The connectivity is read from this source.
+ * \return              The newly created connectivity, or NULL on error.
+ */
+p8est_connectivity_t *p8est_connectivity_source (sc_io_source_t * source);
+
+/** Create new connectivity from a memory buffer.
+ * \param [in] buffer   The connectivity is created from this memory buffer.
+ * \return              The newly created connectivity, or NULL on error.
+ */
+p8est_connectivity_t *p8est_connectivity_inflate (sc_array_t * buffer);
+
+/** Load a connectivity structure from disk.
+ * \param [in] filename         Name of the file to read.
+ * \param [out] bytes           Size in bytes of connectivity on disk or NULL.
+ * \return              Returns valid connectivity, or NULL on file error.
+ */
+p8est_connectivity_t *p8est_connectivity_load (const char *filename,
+                                               size_t * bytes);
+
+/** Create a connectivity structure for the unit cube.
+ */
+p8est_connectivity_t *p8est_connectivity_new_unitcube (void);
+
+/** Create a connectivity structure for an all-periodic unit cube.
+ */
+p8est_connectivity_t *p8est_connectivity_new_periodic (void);
+
+/** Create a connectivity structure for a mostly periodic unit cube.
+ * The left and right faces are identified, and bottom and top rotated.
+ * Front and back are not identified.
+ */
+p8est_connectivity_t *p8est_connectivity_new_rotwrap (void);
+
+/** Create a connectivity structure that contains two cubes.
+ */
+p8est_connectivity_t *p8est_connectivity_new_twocubes (void);
+
+/** Create a connectivity structure that contains two cubes
+ * where the two far ends are identified periodically.
+ */
+p8est_connectivity_t *p8est_connectivity_new_twowrap (void);
+
+/** Create a connectivity structure that contains a few cubes.
+ * These are rotated against each other to stress the topology routines.
+ */
+p8est_connectivity_t *p8est_connectivity_new_rotcubes (void);
+
+/** An m by n by p array with periodicity in x, y, and z if
+ * periodic_a, periodic_b, and periodic_c are true, respectively.
+ */
+p8est_connectivity_t *p8est_connectivity_new_brick (int m, int n, int p,
+                                                    int periodic_a,
+                                                    int periodic_b,
+                                                    int periodic_c);
+
+/** Create a connectivity structure that builds a spherical shell.
+ * It is made up of six connected parts [-1,1]x[-1,1]x[1,2].
+ * This connectivity reuses vertices and relies on a geometry transformation.
+ * It is thus not suitable for p8est_connectivity_complete.
+ */
+p8est_connectivity_t *p8est_connectivity_new_shell (void);
+
+/** Create a connectivity structure that builds a solid sphere.
+ * It is made up of two layers and a cube in the center.
+ * This connectivity reuses vertices and relies on a geometry transformation.
+ * It is thus not suitable for p8est_connectivity_complete.
+ */
+p8est_connectivity_t *p8est_connectivity_new_sphere (void);
+
+/** Create connectivity structure from predefined catalogue.
+ * \param [in]  name            Invokes connectivity_new_* function.
+ *              brick235        brick (2, 3, 5, 0, 0, 0)
+ *              periodic        periodic
+ *              rotcubes        rotcubes
+ *              rotwrap         rotwrap
+ *              shell           shell
+ *              sphere          sphere
+ *              twocubes        twocubes
+ *              twowrap         twowrap
+ *              unit            unitcube
+ * \return      An initialized connectivity if name is defined, NULL else.
+ */
+p8est_connectivity_t *p8est_connectivity_new_byname (const char *name);
+
+/** Fill an array with the axis combination of a face neighbor transform.
+ * \param [in]  iface       The number of the originating face.
+ * \param [in]  nface       Encoded as nface = r * 6 + nf, where nf = 0..5 is
+ *                          the neigbbor's connecting face number and r = 0..3
+ *                          is the relative orientation to the neighbor's face.
+ *                          This encoding matches p8est_connectivity_t.
+ * \param [out] ftransform  This array holds 9 integers.
+ *              [0]..[2]    The coordinate axis sequence of the origin face,
+ *                          the first two referring to the tangentials and the
+ *                          third to the normal.  A permutation of (0, 1, 2).
+ *              [3]..[5]    The coordinate axis sequence of the target face.
+ *              [6]..[8]    Edge reversal flags for tangential axes (boolean);
+ *                          face code in [0, 3] for the normal coordinate q:
+ *                          0: q' = -q
+ *                          1: q' = q + 1
+ *                          2: q' = q - 1
+ *                          3: q' = 2 - q
+ */
+void                p8est_expand_face_transform (int iface, int nface,
+                                                 int ftransform[]);
+
+/** Fill an array with the axis combination of a face neighbor transform.
+ * \param [in]  itree       The number of the originating tree.
+ * \param [in]  iface       The number of the originating tree's face.
+ * \param [out] ftransform  This array holds 9 integers.
+ *              [0]..[2]    The coordinate axis sequence of the origin face.
+ *              [3]..[5]    The coordinate axis sequence of the target face.
+ *              [6]..[8]    Edge reverse flag for axes t1, t2; face code for n.
+ * \return                  The face neighbor tree if it exists, -1 otherwise.
+ */
+p4est_topidx_t      p8est_find_face_transform (p8est_connectivity_t *
+                                               connectivity,
+                                               p4est_topidx_t itree,
+                                               int iface, int ftransform[]);
+
+/** Fills an array with information about edge neighbors.
+ * \param [in] itree    The number of the originating tree.
+ * \param [in] iedge    The number of the originating edge.
+ * \param [in,out] ei   A p8est_edge_info_t structure with initialized array.
+ */
+void                p8est_find_edge_transform (p8est_connectivity_t *
+                                               connectivity,
+                                               p4est_topidx_t itree,
+                                               int iedge,
+                                               p8est_edge_info_t * ei);
+
+/** Fills an array with information about corner neighbors.
+ * \param [in] itree    The number of the originating tree.
+ * \param [in] icorner  The number of the originating corner.
+ * \param [in,out] ci   A p8est_corner_info_t structure with initialized array.
+ */
+void                p8est_find_corner_transform (p8est_connectivity_t *
+                                                 connectivity,
+                                                 p4est_topidx_t itree,
+                                                 int icorner,
+                                                 p8est_corner_info_t * ci);
+
+/** Internally connect a connectivity based on tree_to_vertex information.
+ * Periodicity that is not inherent in the list of vertices will be lost.
+ * \param [in,out] conn     The connectivity needs to have proper vertices
+ *                          and tree_to_vertex fields.  The tree_to_tree
+ *                          and tree_to_face fields must be allocated
+ *                          and satisfy p8est_connectivity_is_valid (conn)
+ *                          but will be overwritten.  The edge and corner
+ *                          fields will be freed and allocated anew.
+ */
+void                p8est_connectivity_complete (p8est_connectivity_t * conn);
+
+/** Removes corner and edge information of a connectivity
+ *  such that enough information is left to run p8est_connectivity_complete successfully.
+ *  The reduced connectivity still passes p8est_connectivity_is_valid.
+ * \param [in,out] conn     The connectivity to be reduced.
+ */
+void                p8est_connectivity_reduce (p8est_connectivity_t * conn);
+
+/** p8est_connectivity_permute
+ * Given a permutation \a perm of the trees in a connectivity \a conn,
+ * permute the trees of \a conn in place and update \a conn to match.
+ * \param [in,out] conn                The connectivity whose trees are
+ *                                     permuted.
+ * \param [in] perm                    A permutation array, whose elements are
+ *                                     size_t's.
+ * \param [in] is_current_to_new       if true, the jth entry of perm is the
+ *                                     new index for the entry whose current
+ *                                     index is j, otherwise the jth entry of
+ *                                     perm is the current index of the tree
+ *                                     whose index will be j after the
+ *                                     permutation.
+ */
+void                p8est_connectivity_permute (p8est_connectivity_t * conn,
+                                                sc_array_t * perm,
+                                                int is_current_to_new);
+
+#ifdef P4EST_WITH_METIS
+
+/** p8est_connectivity_reorder
+ * This function takes a connectivity \a conn and a parameter \a k,
+ * which will typically be the number of processes, and reorders the trees
+ * such that if every processes is assigned (num_trees / k) trees, the
+ * communication volume will be minimized.  This is intended for use with
+ * connectivities that contain a large number of trees.  This should be done
+ * BEFORE a p8est is created using the connectivity.  This is done in place:
+ * any data structures that use indices to refer to trees before this
+ * procedure will be invalid.  Note that this routine calls metis and not
+ * parmetis because the connectivity is copied on every process.
+ * A communicator is required because I'm not positive that metis is
+ * deterministic. \a ctype determines when an edge exist between two trees in
+ * the dual graph used by metis in the reordering.
+ * \param [in]     comm       MPI communicator.
+ * \param [in]     k          if k > 0, the number of pieces metis will use to
+ *                            guide the reordering; if k = 0, the number of
+ *                            pieces will be determined from the MPI
+ *                            communicator.
+ * \param [in,out] conn       connectivity that will be reordered.
+ * \param [in]     ctype      determines when an edge exists in the dual graph
+ *                            of the connectivity structure.
+ */
+void                p8est_connectivity_reorder (MPI_Comm comm, int k,
+                                                p8est_connectivity_t * conn,
+                                                p8est_connect_type_t ctype);
+
+#endif /* P4EST_WITH_METIS */
+
+/** p8est_connectivity_join_faces
+ * This function takes an existing valid connectivity \a conn and modifies it
+ * by joining two tree faces that are currently boundary faces.
+ * \param [in,out] conn        connectivity that will be altered.
+ * \param [in]     tree_left   tree that will be on the left side of the joined
+ *                             faces.
+ * \param [in]     tree_right  tree that will be on the right side of the
+ *                             joined faces.
+ * \param [in]     face_left   face of \a tree_left that will be joined.
+ * \param [in]     face_right  face of \a tree_right that will be joined.
+ * \param [in]     orientation the orientation of \a face_left and
+ *                             \a face_right once joined (see the description
+ *                             of p8est_connectivity_t to understand
+ *                             orientation).
+ */
+void                p8est_connectivity_join_faces (p8est_connectivity_t *
+                                                   conn,
+                                                   p4est_topidx_t tree_left,
+                                                   p4est_topidx_t tree_right,
+                                                   int face_left,
+                                                   int face_right,
+                                                   int orientation);
+
+/** p8est_connectivity_is_equivalent
+ * This function compares two connectivities for equivalence: it returns
+ * \a true if they are the same connectivity, or if they have the same
+ * topology.  The definition of topological sameness is strict: there is no
+ * attempt made to determine whether permutation and/or rotation of the trees
+ * makes the connectivities equivalent.
+ *
+ * \param[in]      conn1    a valid connectivity
+ * \param[out]     conn2    a valid connectivity
+ */
+int                 p8est_connectivity_is_equivalent (p8est_connectivity_t *
+                                                      conn1,
+                                                      p8est_connectivity_t *
+                                                      conn2);
+
+/** Return a pointer to a p8est_edge_transform_t array element. */
+/*@unused@*/
+static inline p8est_edge_transform_t *
+p8est_edge_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_edge_transform_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p8est_edge_transform_t *) (array->array +
+                                     sizeof (p8est_edge_transform_t) * it);
+}
+
+/** Return a pointer to a p8est_corner_transform_t array element. */
+/*@unused@*/
+static inline p8est_corner_transform_t *
+p8est_corner_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_corner_transform_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return
+    (p8est_corner_transform_t *) (array->array +
+                                  sizeof (p8est_corner_transform_t) * it);
+}
+
+/** Read an ABAQUS input file from a file stream.
+ *
+ * This utility function reads a basic ABAQUS file supporting element type with
+ * the prefix C2D4, CPS4, and S4 in 2D and of type C3D8 reading them as
+ * bilinear quadrilateral and trilinear hexahedral trees respectively.
+ *
+ * A basic 2D mesh is given below.  The \c *Node section gives the vertex
+ * number and x, y, and z components for each vertex.  The \c *Element section
+ * gives the 4 vertices in 2D (8 vertices in 3D) of each element in counter
+ * clockwise order. So in 2D the nodes are given as:
+ *
+ *   4                     3
+ *   +-------------------+
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   +-------------------+
+ *   1                   2
+ *
+ * and in 3D they are given as:
+ *
+ * 8                     7
+ *  +---------------------+
+ *  |\                    |\
+ *  | \                   | \
+ *  |  \                  |  \
+ *  |   \                 |   \
+ *  |   5+---------------------+6
+ *  |    |                |    |
+ *  +----|----------------+    |
+ *  4\   |               3 \   |
+ *    \  |                  \  |
+ *     \ |                   \ |
+ *      \|                    \|
+ *       +---------------------+
+ *       1                     2
+ *
+ * \code
+ * *Heading
+ *  box.inp
+ * *Node
+ *     1,    5,   -5,    5
+ *     2,    5,    5,    5
+ *     3,    5,    0,    5
+ *     4,   -5,    5,    5
+ *     5,    0,    5,    5
+ *     6,   -5,   -5,    5
+ *     7,   -5,    0,    5
+ *     8,    0,   -5,    5
+ *     9,    0,    0,    5
+ *    10,    5,    5,   -5
+ *    11,    5,   -5,   -5
+ *    12,    5,    0,   -5
+ *    13,   -5,   -5,   -5
+ *    14,    0,   -5,   -5
+ *    15,   -5,    5,   -5
+ *    16,   -5,    0,   -5
+ *    17,    0,    5,   -5
+ *    18,    0,    0,   -5
+ *    19,   -5,   -5,    0
+ *    20,    5,   -5,    0
+ *    21,    0,   -5,    0
+ *    22,   -5,    5,    0
+ *    23,   -5,    0,    0
+ *    24,    5,    5,    0
+ *    25,    0,    5,    0
+ *    26,    5,    0,    0
+ *    27,    0,    0,    0
+ * *Element, type=C3D8, ELSET=EB1
+ *     1,       6,      19,      23,       7,       8,      21,      27,       9
+ *     2,      19,      13,      16,      23,      21,      14,      18,      27
+ *     3,       7,      23,      22,       4,       9,      27,      25,       5
+ *     4,      23,      16,      15,      22,      27,      18,      17,      25
+ *     5,       8,      21,      27,       9,       1,      20,      26,       3
+ *     6,      21,      14,      18,      27,      20,      11,      12,      26
+ *     7,       9,      27,      25,       5,       3,      26,      24,       2
+ *     8,      27,      18,      17,      25,      26,      12,      10,      24
+ * \endcode
+ *
+ * This code can be called two ways.  The first, when \c vertex==NULL and \c
+ * tree_to_vertex==NULL, is used to count the number of tress and vertices in
+ * the connectivity to be generated by the \c .inp mesh in the \a stream.  The
+ * second, when \c vertices!=NULL and \c tree_to_vertex!=NULL, fill \c vertices
+ * and \c tree_to_vertex.  In this case \c num_vertices and \c num_trees need
+ * to be set to the maximum number of entries allocated in \c vertices and \c
+ * tree_to_vertex.
+ *
+ * \param[in,out]  stream         file stream to read the connectivity from
+ * \param[in,out]  num_vertices   the number of vertices in the connectivity
+ * \param[in,out]  num_trees      the number of trees in the connectivity
+ * \param[out]     vertices       the list of \c vertices of the connectivity
+ * \param[out]     tree_to_vertex the \c tree_to_vertex map of the connectivity
+ *
+ * \returns 0 if successful and nonzero if not
+ */
+
+int                 p8est_connectivity_read_inp_stream (FILE * stream,
+                                                        p4est_topidx_t *
+                                                        num_vertices,
+                                                        p4est_topidx_t *
+                                                        num_trees,
+                                                        double *vertices,
+                                                        p4est_topidx_t *
+                                                        tree_to_vertex);
+
+/** Create a p4est connectivity from an ABAQUS input file.
+ *
+ * This utility function reads a basic ABAQUS file supporting element type with
+ * the prefix C2D4, CPS4, and S4 in 2D and of type C3D8 reading them as
+ * bilinear quadrilateral and trilinear hexahedral trees respectively.
+ *
+ * A basic 2D mesh is given below.  The \c *Node section gives the vertex
+ * number and x, y, and z components for each vertex.  The \c *Element section
+ * gives the 4 vertices in 2D (8 vertices in 3D) of each element in counter
+ * clockwise order. So in 2D the nodes are given as:
+ *
+ *   4                     3
+ *   +-------------------+
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   |                   |
+ *   +-------------------+
+ *   1                   2
+ *
+ * and in 3D they are given as:
+ *
+ * 8                     7
+ *  +---------------------+
+ *  |\                    |\
+ *  | \                   | \
+ *  |  \                  |  \
+ *  |   \                 |   \
+ *  |   5+---------------------+6
+ *  |    |                |    |
+ *  +----|----------------+    |
+ *  4\   |               3 \   |
+ *    \  |                  \  |
+ *     \ |                   \ |
+ *      \|                    \|
+ *       +---------------------+
+ *       1                     2
+ *
+ * \code
+ * *Heading
+ *  box.inp
+ * *Node
+ *     1,    5,   -5,    5
+ *     2,    5,    5,    5
+ *     3,    5,    0,    5
+ *     4,   -5,    5,    5
+ *     5,    0,    5,    5
+ *     6,   -5,   -5,    5
+ *     7,   -5,    0,    5
+ *     8,    0,   -5,    5
+ *     9,    0,    0,    5
+ *    10,    5,    5,   -5
+ *    11,    5,   -5,   -5
+ *    12,    5,    0,   -5
+ *    13,   -5,   -5,   -5
+ *    14,    0,   -5,   -5
+ *    15,   -5,    5,   -5
+ *    16,   -5,    0,   -5
+ *    17,    0,    5,   -5
+ *    18,    0,    0,   -5
+ *    19,   -5,   -5,    0
+ *    20,    5,   -5,    0
+ *    21,    0,   -5,    0
+ *    22,   -5,    5,    0
+ *    23,   -5,    0,    0
+ *    24,    5,    5,    0
+ *    25,    0,    5,    0
+ *    26,    5,    0,    0
+ *    27,    0,    0,    0
+ * *Element, type=C3D8, ELSET=EB1
+ *     1,       6,      19,      23,       7,       8,      21,      27,       9
+ *     2,      19,      13,      16,      23,      21,      14,      18,      27
+ *     3,       7,      23,      22,       4,       9,      27,      25,       5
+ *     4,      23,      16,      15,      22,      27,      18,      17,      25
+ *     5,       8,      21,      27,       9,       1,      20,      26,       3
+ *     6,      21,      14,      18,      27,      20,      11,      12,      26
+ *     7,       9,      27,      25,       5,       3,      26,      24,       2
+ *     8,      27,      18,      17,      25,      26,      12,      10,      24
+ * \endcode
+ *
+ * This function reads a mesh from \a filename and returns an associated p4est
+ * connectivity.
+ *
+ * \param[in]  filename         file to read the connectivity from
+ *
+ * \returns an allocated connectivity associated with the mesh in \a filename
+ */
+p8est_connectivity_t *p8est_connectivity_read_inp (const char *filename);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_CONNECTIVITY_H */
diff --git a/src/p8est_extended.h b/src/p8est_extended.h
new file mode 100644
index 0000000..610afce
--- /dev/null
+++ b/src/p8est_extended.h
@@ -0,0 +1,310 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/********************************************************************
+ *                          IMPORTANT NOTE                          *
+ *                                                                  *
+ * These interfaces are intended for those who like finer control.  *
+ * The API offers extended versions of some basic p4est functions.  *
+ * The API may change without notice.                               *
+ ********************************************************************/
+
+/** \file p8est_extended.h
+ *
+ * Interface routines with extended capabilities.
+ *
+ * \ingroup p8est
+ */
+
+#ifndef P8EST_EXTENDED_H
+#define P8EST_EXTENDED_H
+
+#include <p8est.h>
+#include <p8est_mesh.h>
+#include <p8est_iterate.h>
+
+SC_EXTERN_C_BEGIN;
+
+/* Data pertaining to selecting, inspecting, and profiling algorithms.
+ * A pointer to this structure is hooked into the p8est main structure.
+ *
+ * TODO: Describe the purpose of various switches, counters, and timings.
+ *
+ * The balance_ranges and balance_notify* times are collected
+ * whenever an inspect structure is present in p8est.
+ */
+struct p8est_inspect
+{
+  /** Use sc_ranges to determine the asymmetric communication pattern.
+   * If \a use_balance_ranges is false (the default), sc_notify is used. */
+  int                 use_balance_ranges;
+  /** If true, call both sc_ranges and sc_notify and verify consistency.
+   * Which is actually used is still determined by \a use_balance_ranges. */
+  int                 use_balance_ranges_notify;
+  /** Verify sc_ranges and/or sc_notify as applicable. */
+  int                 use_balance_verify;
+  /** If positive and smaller than p8est_num ranges, overrides it */
+  int                 balance_max_ranges;
+  size_t              balance_A_count_in;
+  size_t              balance_A_count_out;
+  size_t              balance_comm_sent;
+  size_t              balance_comm_nzpeers;
+  size_t              balance_B_count_in;
+  size_t              balance_B_count_out;
+  size_t              balance_zero_sends[2], balance_zero_receives[2];
+  double              balance_A;
+  double              balance_comm;
+  double              balance_B;
+  double              balance_ranges;   /**< time spent in sc_ranges */
+  double              balance_notify;   /**< time spent in sc_notify */
+  /** time spent in sc_notify_allgather */
+  double              balance_notify_allgather;
+  int                 use_B;
+};
+
+/** Callback function prototype to replace one set of quadrants with another.
+ *
+ * This is used by extended routines when the quadrants of an existing, valid
+ * p8est are changed.  The callback allows the user to make changes to newly
+ * initialized quadrants before the quadrants that they replace are destroyed.
+ *
+ * \param [in] num_outgoing The number of outgoing quadrants.
+ * \param [in] outgoing     The outgoing quadrants: after the callback, the
+ *                          user_data, if \a p8est->data_size is nonzero,
+ *                          will be destroyed.
+ * \param [in] num_incoming The number of incoming quadrants.
+ * \param [in,out] incoming The incoming quadrants: prior to the callback,
+ *                          the user_data, if \a p8est->data_size is nonzero,
+ *                          is allocated, and the p8est_init_t callback,
+ *                          if it has been provided, will be called.
+ *
+ * If the mesh is being refined, num_outgoing will be 1 and num_incoming will
+ * be 8, and vice versa if the mesh is being coarsened.
+ */
+typedef void        (*p8est_replace_t) (p8est_t * p8est,
+                                        p4est_topidx_t which_tree,
+                                        int num_outgoing,
+                                        p8est_quadrant_t * outgoing[],
+                                        int num_incoming,
+                                        p8est_quadrant_t * incoming[]);
+
+/** Create a new forest.
+ * This is a more general form of p8est_new.
+ * See the documentation of p8est_new for basic usage.
+ *
+ * \param [in] min_quadrants    Minimum initial quadrants per processor.
+ *                              Makes the refinement pattern mpisize-specific.
+ * \param [in] min_level        The forest is refined at least to this level.
+ *                              May be negative or 0, then it has no effect.
+ * \param [in] fill_uniform     If true, fill the forest with a uniform mesh
+ *                              instead of the coarsest possible one.
+ *                              The latter is partition-specific so that
+ *                              is usually not a good idea.
+ */
+p8est_t            *p8est_new_ext (sc_MPI_Comm mpicomm,
+                                   p8est_connectivity_t * connectivity,
+                                   p4est_locidx_t min_quadrants,
+                                   int min_level, int fill_uniform,
+                                   size_t data_size, p8est_init_t init_fn,
+                                   void *user_pointer);
+
+/** Create a new mesh.
+ * \param [in] p8est                A forest that is fully 2:1 balanced.
+ * \param [in] ghost                The ghost layer created from the
+ *                                  provided p4est.
+ * \param [in] compute_tree_index   Boolean to decide whether to allocate and
+ *                                  compute the quad_to_tree list.
+ * \param [in] compute_level_lists  Boolean to decide whether to compute the
+ *                                  level lists in quad_level.
+ * \param [in] btype                Currently ignored, only face neighbors
+ *                                  are stored.
+ * \return                          A fully allocated mesh structure.
+ */
+p8est_mesh_t       *p8est_mesh_new_ext (p8est_t * p4est,
+                                        p8est_ghost_t * ghost,
+                                        int compute_tree_index,
+                                        int compute_level_lists,
+                                        p8est_connect_type_t btype);
+
+/** Refine a forest with a bounded refinement level and a replace option.
+ * \param [in,out] p8est The forest is changed in place.
+ * \param [in] refine_recursive Boolean to decide on recursive refinement.
+ * \param [in] maxlevel   Maximum allowed refinement level (inclusive).
+ *                        If this is negative the level is restricted only
+ *                        by the compile-time constant QMAXLEVEL in p8est.h.
+ * \param [in] refine_fn  Callback function that must return true if a quadrant
+ *                        shall be refined.  If refine_recursive is true,
+ *                        refine_fn is called for every existing and newly
+ *                        created quadrant.  Otherwise, it is called for every
+ *                        existing quadrant.  It is possible that a refinement
+ *                        request made by the callback is ignored.  To catch
+ *                        this case, you can examine whether init_fn or
+ *                        replace_fn gets called.
+ * \param [in] init_fn    Callback function to initialize the user_data for
+ *                        newly created quadrants, which is guaranteed to be
+ *                        allocated.  This function pointer may be NULL.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace; may be NULL.
+ */
+void                p8est_refine_ext (p8est_t * p8est,
+                                      int refine_recursive, int maxlevel,
+                                      p8est_refine_t refine_fn,
+                                      p8est_init_t init_fn,
+                                      p8est_replace_t replace_fn);
+
+/** Coarsen a forest.
+ * \param [in,out] p8est The forest is changed in place.
+ * \param [in] coarsen_recursive Boolean to decide on recursive coarsening.
+ * \param [in] callback_orphans Boolean to enable calling coarsen_fn even on
+ *                        non-families.  In this case, the second quadrant
+ *                        pointer in the argument list of the callback is NULL,
+ *                        subsequent pointers are undefined, and the return
+ *                        value is ignored.  If coarsen_recursive is true, it
+ *                        is possible that a quadrant is called once or more as
+ *                        an orphan and eventually becomes part of a family.
+ * \param [in] coarsen_fn Callback function that returns true if a
+ *                        family of quadrants shall be coarsened.
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace.
+ */
+void                p8est_coarsen_ext (p8est_t * p8est, int coarsen_recursive,
+                                       int callback_orphans,
+                                       p8est_coarsen_t coarsen_fn,
+                                       p8est_init_t init_fn,
+                                       p8est_replace_t replace_fn);
+
+/** 2:1 balance the size differences of neighboring elements in a forest.
+ * \param [in,out] p8est  The p8est to be worked on.
+ * \param [in] btype      Balance type (face, edge, or corner/full).
+ *                        Corner balance is almost never required when
+ *                        discretizing a PDE; just causes smoother mesh grading.
+ * \param [in] init_fn    Callback function to initialize the user_data
+ *                        which is already allocated automatically.
+ * \param [in] replace_fn Callback function that allows the user to change
+ *                        incoming quadrants based on the quadrants they
+ *                        replace.
+ */
+void                p8est_balance_ext (p8est_t * p8est,
+                                       p8est_connect_type_t btype,
+                                       p8est_init_t init_fn,
+                                       p8est_replace_t replace_fn);
+
+void                p8est_balance_subtree_ext (p8est_t * p8est,
+                                               p8est_connect_type_t btype,
+                                               p4est_topidx_t which_tree,
+                                               p8est_init_t init_fn,
+                                               p8est_replace_t replace_fn);
+
+/** Repartition the forest.
+ *
+ * The forest is partitioned between processors such that each processor
+ * has an approximately equal number of quadrants (or weight).
+ *
+ * \param [in,out] p8est      The forest that will be partitioned.
+ * \param [in]     partition_for_coarsening     If true, the partition
+ *                            is modified to allow one level of coarsening.
+ * \param [in]     weight_fn  A weighting function or NULL
+ *                            for uniform partitioning.
+ * \return         The global number of shipped quadrants
+ */
+p4est_gloidx_t      p8est_partition_ext (p8est_t * p8est,
+                                         int partition_for_coarsening,
+                                         p8est_weight_t weight_fn);
+
+/** p8est_iterate_ext adds the option \a remote: if this is false, then it is
+ * the same as p8est_iterate; if this is true, then corner/edge callbacks are
+ * also called on corners/edges for hanging faces/edges touched by local
+ * quadrants.
+ */
+void                p8est_iterate_ext (p8est_t * p8est,
+                                       p8est_ghost_t * ghost_layer,
+                                       void *user_data,
+                                       p8est_iter_volume_t iter_volume,
+                                       p8est_iter_face_t iter_face,
+                                       p8est_iter_edge_t iter_edge,
+                                       p8est_iter_corner_t iter_corner,
+                                       int remote);
+
+/** Save the complete connectivity/p8est data to disk.  This is a collective
+ * operation that all MPI processes need to call.  All processes write
+ * into the same file, so the filename given needs to be identical over
+ * all parallel invocations.
+ * See p8est_load_ext for information on the autopartition parameter.
+ * \param [in] filename    Name of the file to write.
+ * \param [in] p8est       Valid forest structure.
+ * \param [in] save_data   If true, the element data is saved.
+ *                         Otherwise, a data size of 0 is saved.
+ * \param [in] save_partition   If false, save file as if 1 core was used.
+ *                              If true, save core count and partition.
+ *                         Advantage: Partition can be recovered on loading
+ *                              with same mpisize and autopartition false.
+ *                         Disadvantage: Makes the file depend on mpisize.
+ *                  Either way the file can be loaded with autopartition true.
+ * \note            Aborts on file errors.
+ */
+void                p8est_save_ext (const char *filename, p8est_t * p8est,
+                                    int save_data, int save_partition);
+
+/** Load the complete connectivity/p4est structure from disk.
+ * It is possible to load the file with a different number of processors
+ * than has been used to write it.  The partition will then be uniform.
+ * \param [in] filename         Name of the file to read.
+ * \param [in] mpicomm          A valid MPI communicator.
+ * \param [in] data_size        Size of data for each quadrant which can be
+ *                              zero.  Then user_data_pool is set to NULL.
+ *                              If data_size is zero, load_data is ignored.
+ * \param [in] load_data        If true, the element data is loaded.  This is
+ *                              only permitted if the saved data size matches.
+ *                              If false, the stored data size is ignored.
+ * \param [in] autopartition    Ignore saved partition and make it uniform.
+ * \param [in] broadcasthead    Have only rank 0 read headers and bcast them.
+ * \param [in] user_pointer     Assign to the user_pointer member of the p4est
+ *                              before init_fn is called the first time.
+ * \param [out] connectivity    Connectivity must be destroyed separately.
+ * \return          Returns a valid forest structure. A pointer to a valid
+ *                  connectivity structure is returned through the last
+ *                  argument.
+ * \note            Aborts on file errors or invalid file contents.
+ */
+p8est_t            *p8est_load_ext (const char *filename, sc_MPI_Comm mpicomm,
+                                    size_t data_size, int load_data,
+                                    int autopartition, int broadcasthead,
+                                    void *user_pointer,
+                                    p8est_connectivity_t ** connectivity);
+
+/** The same as p8est_load_ext, but reading the connectivity/p8est from an
+ * open sc_io_source_t stream.
+ */
+p8est_t            *p8est_source_ext (sc_io_source_t * src,
+                                      sc_MPI_Comm mpicomm, size_t data_size,
+                                      int load_data, int autopartition,
+                                      int broadcasthead, void *user_pointer,
+                                      p8est_connectivity_t ** connectivity);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_EXTENDED_H */
diff --git a/src/p8est_geometry.c b/src/p8est_geometry.c
new file mode 100644
index 0000000..0f797f1
--- /dev/null
+++ b/src/p8est_geometry.c
@@ -0,0 +1,300 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * \file p8est_geometry.c
+ * We provide some geometry transformations for the builtin connectivities.
+ * They are not meant as blueprints for future user code.
+ * Please implement p8est_geometry_t as you see fit.
+ */
+
+#include <p4est_to_p8est.h>
+#include "p4est_geometry.c"
+
+typedef enum
+{
+  P8EST_GEOMETRY_BUILTIN_MAGIC = 0x65F2F8DF,
+  P8EST_GEOMETRY_BUILTIN_SHELL,
+  P8EST_GEOMETRY_BUILTIN_SPHERE
+}
+p8est_geometry_builtin_type_t;
+
+typedef struct p8est_geometry_builtin_shell
+{
+  p8est_geometry_builtin_type_t type;
+  double              R2, R1;
+  double              R2byR1, R1sqrbyR2, Rlog;
+}
+p8est_geometry_builtin_shell_t;
+
+typedef struct p8est_geometry_builtin_sphere
+{
+  p8est_geometry_builtin_type_t type;
+  double              R2, R1, R0;
+  double              R2byR1, R1sqrbyR2, R1log;
+  double              R1byR0, R0sqrbyR1, R0log;
+  double              Clength, CdetJ;
+}
+p8est_geometry_builtin_sphere_t;
+
+typedef struct p8est_geometry_builtin
+{
+  /** The geom member needs to come first; we cast to p8est_geometry_t * */
+  p8est_geometry_t    geom;
+  union
+  {
+    p8est_geometry_builtin_type_t type;
+    p8est_geometry_builtin_shell_t shell;
+    p8est_geometry_builtin_sphere_t sphere;
+  }
+  p;
+}
+p8est_geometry_builtin_t;
+
+static void
+p8est_geometry_shell_X (p8est_geometry_t * geom,
+                        p4est_topidx_t which_tree,
+                        const double rst[3], double xyz[3])
+{
+  const struct p8est_geometry_builtin_shell *shell
+    = &((p8est_geometry_builtin_t *) geom)->p.shell;
+  double              x, y, R, q;
+  double              abc[3];
+
+  /* transform from the reference cube into vertex space */
+  p4est_geometry_connectivity_X (geom, which_tree, rst, abc);
+
+  /* assert that input points are in the expected range */
+  P4EST_ASSERT (shell->type == P8EST_GEOMETRY_BUILTIN_SHELL);
+  P4EST_ASSERT (0 <= which_tree && which_tree < 24);
+  P4EST_ASSERT (abc[0] < 1.0 + SC_1000_EPS && abc[0] > -1.0 - SC_1000_EPS);
+  P4EST_ASSERT (abc[1] < 1.0 + SC_1000_EPS && abc[1] > -1.0 - SC_1000_EPS);
+  P4EST_ASSERT (abc[2] < 2.0 + SC_1000_EPS && abc[2] > 1.0 - SC_1000_EPS);
+
+  /* transform abc[0] and y in-place for nicer grading */
+  x = tan (abc[0] * M_PI_4);
+  y = tan (abc[1] * M_PI_4);
+
+  /* compute transformation ingredients */
+  R = shell->R1sqrbyR2 * pow (shell->R2byR1, abc[2]);
+  q = R / sqrt (x * x + y * y + 1.);
+
+  /* assign correct coordinates based on patch id */
+  switch (which_tree / 4) {
+  case 3:                      /* top */
+    xyz[0] = +q * y;
+    xyz[1] = -q * x;
+    xyz[2] = +q;
+    break;
+  case 2:                      /* left */
+    xyz[0] = -q;
+    xyz[1] = -q * x;
+    xyz[2] = +q * y;
+    break;
+  case 1:                      /* bottom */
+    xyz[0] = -q * y;
+    xyz[1] = -q * x;
+    xyz[2] = -q;
+    break;
+  case 0:                      /* right */
+    xyz[0] = +q;
+    xyz[1] = -q * x;
+    xyz[2] = -q * y;
+    break;
+  case 4:                      /* back */
+    xyz[0] = -q * x;
+    xyz[1] = +q;
+    xyz[2] = +q * y;
+    break;
+  case 5:                      /* front */
+    xyz[0] = +q * x;
+    xyz[1] = -q;
+    xyz[2] = +q * y;
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+}
+
+p8est_geometry_t   *
+p8est_geometry_new_shell (p8est_connectivity_t * conn, double R2, double R1)
+{
+  p8est_geometry_builtin_t *builtin;
+  struct p8est_geometry_builtin_shell *shell;
+
+  builtin = P4EST_ALLOC_ZERO (p8est_geometry_builtin_t, 1);
+
+  shell = &builtin->p.shell;
+  shell->type = P8EST_GEOMETRY_BUILTIN_SHELL;
+  shell->R2 = R2;
+  shell->R1 = R1;
+  shell->R2byR1 = R2 / R1;
+  shell->R1sqrbyR2 = R1 * R1 / R2;
+  shell->Rlog = log (R2 / R1);
+
+  builtin->geom.name = "p8est_shell";
+  builtin->geom.user = conn;
+  builtin->geom.X = p8est_geometry_shell_X;
+
+  return (p8est_geometry_t *) builtin;
+}
+
+static void
+p8est_geometry_sphere_X (p8est_geometry_t * geom,
+                         p4est_topidx_t which_tree,
+                         const double rst[3], double xyz[3])
+{
+  const struct p8est_geometry_builtin_sphere *sphere
+    = &((p8est_geometry_builtin_t *) geom)->p.sphere;
+  double              x, y, R, q;
+  double              abc[3];
+
+  /* transform from the reference cube into vertex space */
+  p4est_geometry_connectivity_X (geom, which_tree, rst, abc);
+
+  /* assert that input points are in the expected range */
+  P4EST_ASSERT (sphere->type == P8EST_GEOMETRY_BUILTIN_SPHERE);
+  P4EST_ASSERT (0 <= which_tree && which_tree < 13);
+  P4EST_ASSERT (abc[0] < 1.0 + SC_1000_EPS && abc[0] > -1.0 - SC_1000_EPS);
+  P4EST_ASSERT (abc[1] < 1.0 + SC_1000_EPS && abc[1] > -1.0 - SC_1000_EPS);
+#ifdef P4EST_ENABLE_DEBUG
+  if (which_tree < 12) {
+    P4EST_ASSERT (abc[2] < 2.0 + SC_1000_EPS && abc[2] > 1.0 - SC_1000_EPS);
+  }
+  else {
+    P4EST_ASSERT (abc[2] < 1.0 + SC_1000_EPS && abc[2] > -1.0 - SC_1000_EPS);
+  }
+#endif /* P4EST_ENABLE_DEBUG */
+
+  if (which_tree < 6) {         /* outer shell */
+    const double        z_cmb = abc[2] - (1. + 5. / 8.);
+    const double        dist = 1. / 8.; /* keep it inside the tree */
+
+    x = tan (abc[0] * M_PI_4);
+    y = tan (abc[1] * M_PI_4);
+    if (fabs (z_cmb) < dist) {
+      /* correct z grading for the PREM model */
+      const double        correction = 0.008873;
+
+      R = sphere->R1sqrbyR2 * pow (sphere->R2byR1,
+                                   abc[2] + correction *
+                                   exp (1. / (dist * dist) -
+                                        1. / ((z_cmb + dist) *
+                                              (dist - z_cmb))));
+    }
+    else {
+      R = sphere->R1sqrbyR2 * pow (sphere->R2byR1, abc[2]);
+    }
+    q = R / sqrt (x * x + y * y + 1.);
+  }
+  else if (which_tree < 12) {   /* inner shell */
+    double              p, tanx, tany;
+
+    p = 2. - abc[2];
+    tanx = tan (abc[0] * M_PI_4);
+    tany = tan (abc[1] * M_PI_4);
+    x = p * abc[0] + (1. - p) * tanx;
+    y = p * abc[1] + (1. - p) * tany;
+    R = sphere->R0sqrbyR1 * pow (sphere->R1byR0, abc[2]);
+    q = R / sqrt (1. + (1. - p) * (tanx * tanx + tany * tany) + 2. * p);
+  }
+  else {                        /* center cube */
+    xyz[0] = abc[0] * sphere->Clength;
+    xyz[1] = abc[1] * sphere->Clength;
+    xyz[2] = abc[2] * sphere->Clength;
+
+    return;
+  }
+
+  /* assign correct coordinates based on direction */
+  switch (which_tree % 6) {
+  case 0:                      /* front */
+    xyz[0] = +q * x;
+    xyz[1] = -q;
+    xyz[2] = +q * y;
+    break;
+  case 1:                      /* top */
+    xyz[0] = +q * x;
+    xyz[1] = +q * y;
+    xyz[2] = +q;
+    break;
+  case 2:                      /* back */
+    xyz[0] = +q * x;
+    xyz[1] = +q;
+    xyz[2] = -q * y;
+    break;
+  case 3:                      /* right */
+    xyz[0] = +q;
+    xyz[1] = -q * x;
+    xyz[2] = -q * y;
+    break;
+  case 4:                      /* bottom */
+    xyz[0] = -q * y;
+    xyz[1] = -q * x;
+    xyz[2] = -q;
+    break;
+  case 5:                      /* left */
+    xyz[0] = -q;
+    xyz[1] = -q * x;
+    xyz[2] = +q * y;
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+  }
+}
+
+p8est_geometry_t   *
+p8est_geometry_new_sphere (p8est_connectivity_t * conn,
+                           double R2, double R1, double R0)
+{
+  p8est_geometry_builtin_t *builtin;
+  struct p8est_geometry_builtin_sphere *sphere;
+
+  builtin = P4EST_ALLOC_ZERO (p8est_geometry_builtin_t, 1);
+
+  sphere = &builtin->p.sphere;
+  sphere->type = P8EST_GEOMETRY_BUILTIN_SPHERE;
+  sphere->R2 = R2;
+  sphere->R1 = R1;
+  sphere->R0 = R0;
+
+  /* variables useful for the outer shell */
+  sphere->R2byR1 = R2 / R1;
+  sphere->R1sqrbyR2 = R1 * R1 / R2;
+  sphere->R1log = log (R2 / R1);
+
+  /* variables useful for the inner shell */
+  sphere->R1byR0 = R1 / R0;
+  sphere->R0sqrbyR1 = R0 * R0 / R1;
+  sphere->R0log = log (R1 / R0);
+
+  /* variables useful for the center cube */
+  sphere->Clength = R0 / sqrt (3.);
+  sphere->CdetJ = pow (R0 / sqrt (3.), 3.);
+
+  builtin->geom.name = "p8est_sphere";
+  builtin->geom.user = conn;
+  builtin->geom.X = p8est_geometry_sphere_X;
+
+  return (p8est_geometry_t *) builtin;
+}
diff --git a/src/p8est_geometry.h b/src/p8est_geometry.h
new file mode 100644
index 0000000..1518c59
--- /dev/null
+++ b/src/p8est_geometry.h
@@ -0,0 +1,104 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_geometry.h transforms from vertex frame to physical space
+ *
+ * \ingroup p8est
+ */
+
+#ifndef P8EST_GEOMETRY_H
+#define P8EST_GEOMETRY_H
+
+#include <p8est_connectivity.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef struct p8est_geometry p8est_geometry_t;
+
+/** Forward transformation from the reference unit square to physical space.
+ * The physical space "xyz" is user-defined, currently used for VTK output.
+ */
+typedef void        (*p8est_geometry_X_t) (p8est_geometry_t * geom,
+                                           p4est_topidx_t which_tree,
+                                           const double abc[3],
+                                           double xyz[3]);
+
+/** Destructor prototype for a user-allocated \a p8est_geometry_t.
+ * It is invoked by p8est_geometry_destroy.  If the user chooses to
+ * reserve the structure statically, simply don't call p4est_geometry_destroy.
+ */
+typedef void        (*p8est_geometry_destroy_t) (p8est_geometry_t * geom);
+
+/** This structure can be created by the user,
+ * p4est will never change its contents.
+ */
+struct p8est_geometry
+{
+  const char         *name;     /**< User's choice is arbitrary. */
+  void               *user;     /**< User's choice is arbitrary. */
+  p8est_geometry_X_t  X;        /**< Coordinate transformation. */
+  p8est_geometry_destroy_t destroy;     /**< Destructor called by
+                                             p8est_geometry_destroy.  If
+                                             NULL, P4EST_FREE is called. */
+};
+
+/** Can be used to conveniently destroy a geometry structure.
+ * The user is free not to call this function at all if they handle the
+ * memory of the p8est_geometry_t in their own way.
+ */
+void                p8est_geometry_destroy (p8est_geometry_t * geom);
+
+/** Create a geometry structure based on the vertices in a connectivity.
+ * The transformation is constructed using trilinear interpolation.
+ * \param [in] conn A p8est_connectivity_t with valid vertices.  We do NOT
+ *                  take ownership and expect this structure to stay alive.
+ * \return          Geometry structure; use with p4est_geometry_destroy.
+ */
+p8est_geometry_t   *p8est_geometry_new_connectivity (p8est_connectivity_t *
+                                                     conn);
+
+/** Create a geometry structure for the spherical shell of 24 trees.
+ * \param [in] conn Result of p8est_connectivity_new_shell or equivalent.
+ *                  We do NOT take ownership and expect it to stay alive.
+ * \param [in] R2   The outer radius of the shell.
+ * \param [in] R1   The inner radius of the shell.
+ * \return          Geometry structure; use with p4est_geometry_destroy.
+ */
+p8est_geometry_t   *p8est_geometry_new_shell (p8est_connectivity_t * conn,
+                                              double R2, double R1);
+
+/** Create a geometry structure for the solid sphere of 13 trees.
+ * \param [in] conn Result of p8est_connectivity_new_sphere or equivalent.
+ *                  We do NOT take ownership and expect it to stay alive.
+ * \param [in] R2   The outer radius of the sphere.
+ * \param [in] R1   The outer radius of the inner shell.
+ * \param [in] R0   The inner radius of the inner shell.
+ * \return          Geometry structure; use with p4est_geometry_destroy.
+ */
+p8est_geometry_t   *p8est_geometry_new_sphere (p8est_connectivity_t * conn,
+                                               double R2, double R1,
+                                               double R0);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_GEOMETRY_H */
diff --git a/src/p8est_ghost.c b/src/p8est_ghost.c
new file mode 100644
index 0000000..c7c4ae3
--- /dev/null
+++ b/src/p8est_ghost.c
@@ -0,0 +1,280 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include <p8est_bits.h>
+#include <p8est_communication.h>
+
+#ifdef P4EST_ENABLE_MPI
+
+/** Gets the procids of the owners of \a q.
+ *
+ * For a quadrant across the edge of a tree has possibly multiple
+ * trees in which it lives, and thus multiple procs.
+ *
+ * \param [in]     p4est    The forest in which to search for \a q.
+ * \param [in]     treeid   The tree id to which \a q belongs.
+ * \param [in]     edge     The edge of the tree \a q is across from.
+ * \param [in]     q        The quadrant that is being searched for.
+ * \param [in,out] qprocs   Starts as an initialized array and ends with
+ *                          the list of processors that \a q belongs too.
+ * \param [out]    nurgood  If not NULL, check if the smallest quadrant
+ *                          in the upper right corner of q after transform
+ *                          has the same owner.
+ */
+static void
+p8est_quadrant_find_tree_edge_owners (p4est_t * p4est,
+                                      p4est_topidx_t treeid,
+                                      int edge,
+                                      const p4est_quadrant_t * q,
+                                      sc_array_t * q_procs, int *nurgood)
+{
+  const int           rank = p4est->mpirank;
+  int                *proc, nurproc;
+  size_t              etree;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  p4est_quadrant_t    eq;
+  p8est_edge_info_t   ei;
+  p8est_edge_transform_t *et;
+  sc_array_t         *eta;
+
+  P4EST_ASSERT (p8est_quadrant_is_outside_edge (q));
+
+  P4EST_QUADRANT_INIT (&eq);
+
+  /* Find all edges that are not myself or from a face neighbor */
+  eta = &ei.edge_transforms;
+  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+  p8est_find_edge_transform (conn, treeid, edge, &ei);
+
+  sc_array_resize (q_procs, 0);
+  if (nurgood != NULL) {
+    *nurgood = 1;
+    if (q->level == P4EST_QMAXLEVEL)
+      nurgood = NULL;
+  }
+
+  for (etree = 0; etree < eta->elem_count; ++etree) {
+    et = p8est_edge_array_index (eta, etree);
+
+    p8est_quadrant_transform_edge (q, &eq, &ei, et, 1);
+
+    proc = (int *) sc_array_push (q_procs);
+    *proc = p4est_comm_find_owner (p4est, et->ntree, &eq, rank);
+
+    if (nurgood != NULL) {
+      p4est_quadrant_last_descendant (&eq, &eq, P4EST_QMAXLEVEL);
+      nurproc = p4est_comm_find_owner (p4est, et->ntree, &eq, *proc);
+      *nurgood = *nurgood && (nurproc == *proc);
+    }
+  }
+
+  sc_array_reset (eta);
+}
+
+#endif /* P4EST_ENABLE_MPI */
+
+/** Get the small edge neighbors of \a q.
+ *
+ * Gets the two small edge neighbors, which are half of the size assuming
+ * the 2-1 constant.
+ *
+ * The order of the \a n[i] is given in the Morton ordering.
+ *
+ * \param [in]  q      The quadrant whose face neighbors will be constructed.
+ * \param [in]  edge   The edge across which to generate the neighbors.
+ * \param [out] n[0],n[1] Filled with the two smaller face neighbors.
+ * \param [out] nur[0],nur[1] If not NULL, filled with smallest quadrants
+ *                     that fit in the upper right corners of \a n.
+ */
+static void
+p8est_quadrant_get_half_edge_neighbors (const p4est_quadrant_t * q,
+                                        int edge, p4est_quadrant_t n[],
+                                        p4est_quadrant_t nur[])
+{
+  const p4est_qcoord_t qh = P4EST_QUADRANT_LEN (q->level);
+  const p4est_qcoord_t qh_2 = P4EST_QUADRANT_LEN (q->level + 1);
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+  P4EST_ASSERT (q->level < P4EST_QMAXLEVEL);
+  P4EST_ASSERT (0 <= edge && edge < 12);
+
+  switch (edge / 4) {
+  case 0:
+    n[0].x = n[1].x = q->x;
+    n[0].y = n[1].y = q->y + (!(edge & 0x01) ? -qh_2 : qh);
+    n[0].z = n[1].z = q->z + (!(edge & 0x02) ? -qh_2 : qh);
+    n[1].x += qh_2;
+    break;
+  case 1:
+    n[0].x = n[1].x = q->x + (!(edge & 0x01) ? -qh_2 : qh);
+    n[0].y = n[1].y = q->y;
+    n[0].z = n[1].z = q->z + (!(edge & 0x02) ? -qh_2 : qh);
+    n[1].y += qh_2;
+    break;
+  case 2:
+    n[0].x = n[1].x = q->x + (!(edge & 0x01) ? -qh_2 : qh);
+    n[0].y = n[1].y = q->y + (!(edge & 0x02) ? -qh_2 : qh);
+    n[0].z = n[1].z = q->z;
+    n[1].z += qh_2;
+    break;
+  default:
+    SC_ABORT_NOT_REACHED ();
+    break;
+  }
+  n[0].level = n[1].level = (int8_t) (q->level + 1);
+  P4EST_ASSERT (p4est_quadrant_is_extended (&n[0]));
+  P4EST_ASSERT (p4est_quadrant_is_extended (&n[1]));
+
+  if (nur != NULL) {
+    const p4est_qcoord_t dh = qh_2 - P4EST_QUADRANT_LEN (P4EST_QMAXLEVEL);
+
+    nur[0].x = n[0].x + dh;
+    nur[0].y = n[0].y + dh;
+    nur[0].z = n[0].z + dh;
+    nur[0].level = P4EST_QMAXLEVEL;
+    P4EST_ASSERT (p4est_quadrant_is_extended (&nur[0]));
+    nur[1].x = n[1].x + dh;
+    nur[1].y = n[1].y + dh;
+    nur[1].z = n[1].z + dh;
+    nur[1].level = P4EST_QMAXLEVEL;
+    P4EST_ASSERT (p4est_quadrant_is_extended (&nur[1]));
+  }
+}
+
+/** Get all possible edge neighbors of \a q.
+ *
+ * Gets the edge neighbors, possible assuming the 2-1 constraint.
+ * If the larger quadrant doesn't exist than it is returned
+ * as initialized by P4EST_QUADRANT_INIT.
+ *
+ * The order of \a n[0], \a n[1] is given in Morton ordering.
+ *
+ * \param [in]  q      The quadrant whose edge neighbors will be constructed.
+ * \param [in]  edge   The edge across which to generate the neighbors.
+ * \param [out] n[0],n[1] Filled with the smaller possible edge neighbors,
+ *                     which are half of the size if they exist
+ *                     or initialized to P4EST_QUADRANT_INIT.
+ * \param [out] n[2]   Filled with the edge neighbor, which is the same size.
+ * \param [out] n[3]   Filled with the edge neighbor, which is twice the size
+ *                     if it exists or initialized to P4EST_QUADRANT_INIT.
+ */
+static void
+p8est_quadrant_get_possible_edge_neighbors (const p4est_quadrant_t * q,
+                                            int edge, p4est_quadrant_t n[])
+{
+  const int           qcid = p4est_quadrant_child_id (q);
+  p4est_quadrant_t   *r = &n[3];
+
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  if (q->level == P4EST_QMAXLEVEL) {
+    P4EST_QUADRANT_INIT (&n[0]);
+    P4EST_QUADRANT_INIT (&n[1]);
+  }
+  else {
+    p8est_quadrant_get_half_edge_neighbors (q, edge, n, NULL);
+  }
+
+  p8est_quadrant_edge_neighbor (q, edge, &n[2]);
+
+  /* Check to see if the larger neighbor exists */
+  if ((qcid != p8est_edge_corners[edge][0] &&
+       qcid != p8est_edge_corners[edge][1]) || q->level == 0) {
+    P4EST_QUADRANT_INIT (r);
+  }
+  else {
+    p4est_quadrant_parent (q, r);
+    p8est_quadrant_edge_neighbor (r, edge, r);
+  }
+}
+
+/** Checks if a quadrant's edge is on the boundary of the forest.
+ *
+ * This means that the quadrant's tree doesn't have any non face neighbors.
+ *
+ * \param [in] p4est  The forest in which to search for \a q
+ * \param [in] treeid The tree id for which \a q belongs.
+ * \param [in] q      The quadrant that is in question.
+ * \param [in] edge   The edge of quadrant that is in question.
+ *
+ * \return true if the quadrant's edge is on the boundary of the forest and
+ *         false otherwise.
+ */
+static int
+p8est_quadrant_on_edge_boundary (p4est_t * p4est, p4est_topidx_t treeid,
+                                 int edge, const p4est_quadrant_t * q)
+{
+  int                 face;
+  int                 on_boundary;
+  p4est_quadrant_t    q2;
+  p4est_connectivity_t *conn = p4est->connectivity;
+  p8est_edge_info_t   ei;
+  sc_array_t         *eta;
+
+  P4EST_ASSERT (0 <= edge && edge < 12);
+  P4EST_ASSERT (p4est_quadrant_is_valid (q));
+
+  if (p8est_quadrant_touches_edge (q, edge, 1)) {
+    eta = &ei.edge_transforms;
+    sc_array_init (eta, sizeof (p8est_edge_transform_t));
+    p8est_find_edge_transform (conn, treeid, edge, &ei);
+
+    on_boundary = (eta->elem_count == 0);
+    sc_array_reset (eta);
+
+    return on_boundary;
+  }
+
+  P4EST_QUADRANT_INIT (&q2);
+  p8est_quadrant_edge_neighbor (q, edge, &q2);
+  P4EST_ASSERT (!p4est_quadrant_is_outside_corner (&q2));
+  P4EST_ASSERT (!p8est_quadrant_is_outside_edge (&q2));
+  if (q2.x < 0) {
+    face = 0;
+  }
+  else if (q2.x >= P4EST_ROOT_LEN) {
+    face = 1;
+  }
+  else if (q2.y < 0) {
+    face = 2;
+  }
+  else if (q2.y >= P4EST_ROOT_LEN) {
+    face = 3;
+  }
+  else if (q2.z < 0) {
+    face = 4;
+  }
+  else if (q2.z >= P4EST_ROOT_LEN) {
+    face = 5;
+  }
+  else {
+    return 0;
+  }
+
+  return
+    (conn->tree_to_tree[P4EST_FACES * treeid + face] == treeid &&
+     (int) conn->tree_to_face[P4EST_FACES * treeid + face] == face);
+}
+
+#include "p4est_ghost.c"
diff --git a/src/p8est_ghost.h b/src/p8est_ghost.h
new file mode 100644
index 0000000..67fe21f
--- /dev/null
+++ b/src/p8est_ghost.h
@@ -0,0 +1,304 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_ghost.h
+ *
+ * passing quadrants and data to neighboring processes
+ *
+ * \ingroup p8est
+ */
+
+#ifndef P8EST_GHOST_H
+#define P8EST_GHOST_H
+
+#include <p8est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** quadrants that neighbor the local domain */
+typedef struct
+{
+  int                 mpisize;
+  p4est_topidx_t      num_trees;
+  p8est_connect_type_t btype; /**< which neighbors are in the ghost layer */
+
+  /** An array of quadrants which make up the ghost layer around \a
+   * p4est.  Their piggy3 data member is filled with their owner's tree
+   * and local number (cumulative over trees).  Quadrants are ordered in \c
+   * p8est_quadrant_compare_piggy order.  These are quadrants inside the
+   * neighboring tree, i.e., \c p8est_quadrant_is_inside() is true for the
+   * quadrant and the neighboring tree.
+   */
+  sc_array_t          ghosts; /**< array of p8est_quadrant_t type */
+  p4est_locidx_t     *tree_offsets;     /**< num_trees + 1 ghost indices */
+  p4est_locidx_t     *proc_offsets;     /**< mpisize + 1 ghost indices */
+
+  /** An array of local quadrants that touch the parallel boundary from the
+   * inside, i.e., that are ghosts in the perspective of at least one other
+   * processor.  The storage convention is the same as for \c ghosts above.
+   */
+  sc_array_t          mirrors; /**< array of p4est_quadrant_t type */
+  p4est_locidx_t     *mirror_tree_offsets;      /**< num_trees + 1 mirror indices */
+  p4est_locidx_t     *mirror_proc_mirrors;      /**< indices into mirrors grouped by
+                                                   outside processor rank and
+                                                   ascending within each rank */
+  p4est_locidx_t     *mirror_proc_offsets;      /**< mpisize + 1 indices into 
+                                                   mirror_proc_mirrors */
+  p4est_locidx_t     *mirror_proc_fronts;       /**< like mirror_proc_mirrors,
+                                                   but limited to the
+                                                   outermost octants.  This is
+                                                   NULL until
+                                                   p4est_ghost_expand is
+                                                   called */
+  p4est_locidx_t     *mirror_proc_front_offsets;        /**< NULL until
+                                                           p4est_ghost_expand is
+                                                           called */
+}
+p8est_ghost_t;
+
+/** Examine if a ghost structure is valid as desribed above.
+ * Test if within a ghost-structure the arrays ghosts and mirrors are in
+ * p4est_quadrant_compare_piggy order.
+ * Test if local_num in piggy3 data member of the quadrants in ghosts and
+ * mirrors are in ascending order (ascending within each rank for ghost).
+ *
+ * Test if the p4est_locidx_t arrays are in ascending order
+ * (for mirror_proc_mirrors ascending within each rank)
+ * \param [in] p8est    the forest.
+ * \param [in] ghost    Ghost layer structure.
+ * \return true if \a ghost is valid
+ */
+int                 p8est_ghost_is_valid (p8est_t * p8est, p8est_ghost_t *ghost);
+
+
+/** Calculate the memory usage of the ghost layer.
+ * \param [in] ghost    Ghost layer structure.
+ * \return              Memory used in bytes.
+ */
+size_t              p8est_ghost_memory_used (p8est_ghost_t * ghost);
+
+/** Gets the processor id of a quadrant's owner.
+ * The quadrant can lie outside of a tree across faces (and only faces).
+ *
+ * \param [in] p8est  The forest in which to search for a quadrant.
+ * \param [in] treeid The tree to which the quadrant belongs.
+ * \param [in] face   Supply a face direction if known, or -1 otherwise.
+ * \param [in] q      The quadrant that is being searched for.
+ *
+ * \return Processor id of the owner
+ *                or -1 if the quadrant lies outside of the mesh.
+ *
+ * \warning Does not work for tree edge or corner neighbors.
+ */
+int                 p8est_quadrant_find_owner (p8est_t * p8est,
+                                               p4est_topidx_t treeid,
+                                               int face,
+                                               const p8est_quadrant_t * q);
+
+/** Builds the ghost layer.
+ *
+ * This will gather the quadrants from each neighboring proc to build one layer
+ * of face, edge and corner based ghost elements around the ones they own.
+ *
+ * \param [in] p8est            The forest for which the ghost layer will be
+ *                              generated.
+ * \param [in] btype            Which ghosts to include (across face, edge,
+ *                              or corner/full).
+ * \return                      A fully initialized ghost layer.
+ */
+p8est_ghost_t      *p8est_ghost_new (p8est_t * p8est,
+                                     p8est_connect_type_t btype);
+
+/** Frees all memory used for the ghost layer. */
+void                p8est_ghost_destroy (p8est_ghost_t * ghost);
+
+/** Conduct binary search for exact match on a range of the ghost layer.
+ * \param [in] ghost            The ghost layer.
+ * \param [in] which_proc       The owner of the searched quadrant.  Can be -1.
+ * \param [in] which_tree       The tree of the searched quadrant.  Can be -1.
+ * \param [in] q                Valid quadrant is searched in the ghost layer.
+ * \return                      Offset in the ghost layer, or -1 if not found.
+ */
+ssize_t             p8est_ghost_bsearch (p8est_ghost_t * ghost,
+                                         int which_proc,
+                                         p4est_topidx_t which_tree,
+                                         const p8est_quadrant_t * q);
+
+/** Conduct binary search for ancestor on range of the ghost layer.
+ * \param [in] ghost            The ghost layer.
+ * \param [in] which_proc       The owner of the searched quadrant.  Can be -1.
+ * \param [in] which_tree       The tree of the searched quadrant.  Can be -1.
+ * \param [in] q                Valid quadrant's ancestor is searched.
+ * \return                      Offset in the ghost layer, or -1 if not found.
+ */
+ssize_t             p8est_ghost_tree_contains (p8est_ghost_t * ghost,
+                                               int which_proc,
+                                               p4est_topidx_t which_tree,
+                                               const p8est_quadrant_t * q);
+
+/** Checks if quadrant exists in the local forest or the ghost layer.
+ *
+ * For quadrants across tree boundaries it checks if the quadrant exists
+ * across any face, but not across edges or corners.
+ *
+ * \param [in]  p8est        The forest in which to search for \a q.
+ * \param [in]  ghost        The ghost layer in which to search for \a q.
+ * \param [in]  treeid       The tree to which \a q belongs.
+ * \param [in]  q            The quadrant that is being searched for.
+ * \param [in,out] face      On input, face id across which \a q was created.
+ *                           On output, the neighbor's face number augmented
+ *                           by orientation, so face is in 0..23.
+ * \param [in,out] hang      If not NULL, signals that q is bigger than
+ *                           the quadrant it came from.  The child id
+ *                           of that originating quadrant is passed into hang.
+ *                           On output, hang holds the hanging face number
+ *                           of \a q that is in contact with its originator.
+ * \param [out] owner_rank   Filled with the rank of the owner if it is found
+ *                           and undefined otherwise.
+ *
+ * \return      Returns the local number of \a q if the quadrant exists
+ *              in the local forest or in the ghost_layer.  Otherwise,
+ *              returns -2 for a domain boundary and -1 if not found.
+ */
+p4est_locidx_t      p8est_face_quadrant_exists (p8est_t * p8est,
+                                                p8est_ghost_t * ghost,
+                                                p4est_topidx_t treeid,
+                                                const p8est_quadrant_t * q,
+                                                int *face, int *hang,
+                                                int *owner_rank);
+
+/** Checks if quadrant exists in the local forest or the ghost layer.
+ *
+ * For quadrants across tree corners it checks if the quadrant exists
+ * in any of the corner neighbors, thus it can execute multiple queries.
+ *
+ * \param [in]  p4est        The forest in which to search for \a q
+ * \param [in]  ghost        The ghost layer in which to search for \a q
+ * \param [in]  treeid       The tree to which \a q belongs (can be extended).
+ * \param [in]  q            The quadrant that is being searched for.
+ * \param [in,out] exists_arr Must exist and be of of elem_size = sizeof (int)
+ *                           for inter-tree corner cases.  Is resized by this
+ *                           function to one entry for each corner search
+ *                           and set to true/false depending on its existence
+ *                           in the local forest or ghost_layer.
+ * \param [in,out] rproc_arr If not NULL is filled with one rank per query.
+ * \param [in,out] rquad_arr If not NULL is filled with one quadrant per query.
+ *                           Its piggy3 member is defined as well.
+ *
+ * \return true if the quadrant exists in the local forest or in the
+ *                  ghost_layer, and false if doesn't exist in either.
+ */
+int                 p8est_quadrant_exists (p8est_t * p8est,
+                                           p8est_ghost_t * ghost,
+                                           p4est_topidx_t treeid,
+                                           const p8est_quadrant_t * q,
+                                           sc_array_t * exists_arr,
+                                           sc_array_t * rproc_arr,
+                                           sc_array_t * rquad_arr);
+
+/** Check a forest to see if it is balanced.
+ *
+ * This function builds the ghost layer and discards it when done.
+ *
+ * \param [in] p8est    The p8est to be tested.
+ * \param [in] btype    Balance type (face, edge, corner or default, full).
+ * \return Returns true if balanced, false otherwise.
+ */
+int                 p8est_is_balanced (p8est_t * p8est,
+                                       p8est_connect_type_t btype);
+
+/** Compute the parallel checksum of a ghost layer.
+ * \param [in] p8est   The MPI information of this p8est will be used.
+ * \param [in] ghost   A ghost layer obtained from the p8est.
+ * \return             Parallel checksum on rank 0, 0 otherwise.
+ */
+unsigned            p8est_ghost_checksum (p8est_t * p8est,
+                                          p8est_ghost_t * ghost);
+
+/** Transfer data for local quadrants that are ghosts to other processors.
+ * Send the data stored in the quadrant's user_data.  This is either the
+ * pointer variable itself if \c p8est->data_size is 0, or the content of
+ * the referenced memory field if p8est->data_size is positive.
+ * \param [in] p8est            The forest used for reference.
+ * \param [in] ghost            The ghost layer used for reference.
+ * \param [in,out] ghost_data   Pre-allocated contiguous data for all ghost
+ *                              quadrants in sequence.  If p8est->data_size is
+ *                              0, must at least hold sizeof (void *) bytes for
+ *                              each, otherwise p8est->data_size each.
+ */
+void                p8est_ghost_exchange_data (p8est_t * p4est,
+                                               p8est_ghost_t * ghost,
+                                               void *ghost_data);
+
+/** Transfer data for local quadrants that are ghosts to other processors.
+ * The data size is the same for all quadrants and can be chosen arbitrarily.
+ * \param [in] p8est            The forest used for reference.
+ * \param [in] ghost            The ghost layer used for reference.
+ * \param [in] data_size        The data size to transfer per quadrant.
+ * \param [in] mirror_data      One data pointer per mirror quadrant. 
+ * \param [in,out] ghost_data   Pre-allocated contiguous data for all ghosts
+ *                              in sequence, which must hold at least \c
+ *                              data_size for each ghost.
+ */
+void                p8est_ghost_exchange_custom (p8est_t * p4est,
+                                                 p8est_ghost_t * ghost,
+                                                 size_t data_size,
+                                                 void **mirror_data,
+                                                 void *ghost_data);
+
+/** Transfer data for local quadrants that are ghosts to other processors.
+ * The data size is the same for all quadrants and can be chosen arbitrarily.
+ * This function restricts the transfer to a range of refinement levels.
+ * The memory for quadrants outside the level range is not dereferenced.
+ * \param [in] p4est            The forest used for reference.
+ * \param [in] ghost            The ghost layer used for reference.
+ * \param [in] minlevel         Level of the largest quads to be exchanged.
+ *                              Use <= 0 for no restriction.
+ * \param [in] maxlevel         Level of the smallest quads to be exchanged.
+ *                              Use >= P4EST_QMAXLEVEL for no restriction.
+ * \param [in] data_size        The data size to transfer per quadrant.
+ * \param [in] mirror_data      One data pointer per mirror quadrant as input. 
+ * \param [in,out] ghost_data   Pre-allocated contiguous data for all ghosts
+ *                              in sequence, which must hold at least \c
+ *                              data_size for each ghost.
+ */
+void                p8est_ghost_exchange_custom_levels (p8est_t * p8est,
+                                                        p8est_ghost_t * ghost,
+                                                        int minlevel,
+                                                        int maxlevel,
+                                                        size_t data_size,
+                                                        void **mirror_data,
+                                                        void *ghost_data);
+
+/** Expand the size of the ghost layer and mirrors by one additional layer of
+ * adjacency.
+ * \param [in] p8est            The forest from which the ghost layer was
+ *                              generated.
+ * \param [in,out] ghost        The ghost layer to be expanded.
+ */
+void                p8est_ghost_expand (p8est_t * p8est,
+                                        p8est_ghost_t * ghost);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_GHOST_H */
diff --git a/src/p8est_io.c b/src/p8est_io.c
new file mode 100644
index 0000000..c294560
--- /dev/null
+++ b/src/p8est_io.c
@@ -0,0 +1,26 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Copyright (C) 2012 Carsten Burstedde
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_io.c"
diff --git a/src/p8est_io.h b/src/p8est_io.h
new file mode 100644
index 0000000..e21ad16
--- /dev/null
+++ b/src/p8est_io.h
@@ -0,0 +1,68 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Copyright (C) 2012 Carsten Burstedde
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P8EST_IO_H
+#define P8EST_IO_H
+
+#include <p8est.h>
+
+/** Extract processor local quadrants' x y z level data.
+ * Optionally extracts the quadrant data as well into a separate array.
+ * \param [in] p8est    The forest is not modified.
+ * \param [in,out] data If not NULL, pointer to a pointer that will be set
+ *                      to a newly allocated array with per-quadrant data.
+ *                      Must be NULL if p4est->data_size == 0.
+ * \return              An array of type p8est_qcoord_t that contains
+ *                      x y z level for each quadrant on this processor.
+ *                      The tree information is not extracted.
+ */
+sc_array_t         *p8est_deflate_quadrants (p8est_t * p8est,
+                                             sc_array_t ** data);
+
+/** Create a new p4est based on serialized data.
+ * See p8est.h and p8est_communication.h for more information on parameters.
+ * \param [in] mpicomm       A valid MPI communicator.
+ * \param [in] connectivity  This is the connectivity information that
+ *                           the forest is built with.  Note that p4est
+ *                           does not take ownership of the memory.
+ * \param [in] global_first_quadrant First global quadrant on each proc and
+ *                           one beyond.  Copied into global_first_quadrant.
+ *                           Local count on rank is gfq[rank + 1] - gfq[rank].
+ * \param [in] pertree       The cumulative quadrant counts per tree.
+ * \param [in] quadrants     Array as returned by p8est_deflate_quadrants.
+ * \param [in] data          Array as from p8est_deflate_quadrants or NULL.
+ *                           The elem_size of this array informs data_size.
+ *                           Its elem_count equals the number of local quads.
+ * \param [in] user_pointer  Assign to the user_pointer member of the p4est.
+ * \return              The newly created p4est.
+ */
+p8est_t            *p8est_inflate (sc_MPI_Comm mpicomm,
+                                   p8est_connectivity_t * connectivity,
+                                   const p4est_gloidx_t *
+                                   global_first_quadrant,
+                                   const p4est_gloidx_t * pertree,
+                                   sc_array_t * quadrants, sc_array_t * data,
+                                   void *user_pointer);
+
+#endif /* !P8EST_IO_H */
diff --git a/src/p8est_iterate.c b/src/p8est_iterate.c
new file mode 100644
index 0000000..5623787
--- /dev/null
+++ b/src/p8est_iterate.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_iterate.c"
diff --git a/src/p8est_iterate.h b/src/p8est_iterate.h
new file mode 100644
index 0000000..ae0b888
--- /dev/null
+++ b/src/p8est_iterate.h
@@ -0,0 +1,403 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_iterate.h
+ *
+ * Iteration over mesh topology via callbacks
+ *
+ * \ingroup p8est
+ */
+
+#ifndef P8EST_ITERATE_H
+#define P8EST_ITERATE_H
+
+#include <p8est.h>
+#include <p8est_ghost.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** The information that is available to the user-defined p8est_iter_volume_t
+ * callback function.
+ *
+ * \a treeid gives the index in \a p4est->trees of the tree to which
+ *    \a quad belongs.
+ * \a quadid gives the index of \a quad within \a tree's quadrants array.
+ */
+typedef struct p8est_iter_volume_info
+{
+  p8est_t            *p4est;
+  p8est_ghost_t      *ghost_layer;
+  p8est_quadrant_t   *quad;    /**< the quadrant of the callback */
+  p4est_locidx_t      quadid;  /**< id in \a quad's tree array (see
+                                    p8est_tree_t) */
+  p4est_topidx_t      treeid;  /**< the tree containing \a quad */
+}
+p8est_iter_volume_info_t;
+
+/** The prototype for a function that p8est_iterate() will execute at every
+ * quadrant local to the current process.
+ * \param [in] info          information about a quadrant provided to the user
+ * \param [in,out] user_data the user context passed to p8est_iterate()
+ */
+typedef void        (*p8est_iter_volume_t) (p8est_iter_volume_info_t * info,
+                                            void *user_data);
+
+/** Information about one side of a face in the forest.  If a \a quad is local
+ * (\a is_ghost is false), then its \a quadid indexes the tree's quadrant array;
+ * otherwise, it indexes the ghosts array. If the face is hanging, then the
+ * quadrants are listed in z-order. If a quadrant should be present, but it is
+ * not included in the ghost layer, then quad = NULL, is_ghost is true, and
+ * quadid = -1.
+ */
+typedef struct p8est_iter_face_side
+{
+  p4est_topidx_t      treeid;          /**< the tree on this side */
+  int8_t              face;            /**< which quadrant side the face
+                                            touches */
+  int8_t              is_hanging;      /**< boolean: one full quad (0) or
+                                            four smaller quads (1) */
+  union p8est_iter_face_side_data
+  {
+    struct
+    {
+      int8_t              is_ghost;    /**< boolean: local (0) or ghost (1) */
+      p8est_quadrant_t   *quad;        /**< the actual quadrant */
+      p4est_locidx_t      quadid;      /**< index in tree or ghost array */
+    }
+    full; /**< if \a is_hanging = 0,
+               use is.full to access per-quadrant data */
+    struct
+    {
+      int8_t              is_ghost[4]; /**< boolean: local (0) or ghost (1) */
+      p8est_quadrant_t   *quad[4];     /**< the actual quadrant */
+      p4est_locidx_t      quadid[4];   /**< index in tree or ghost array */
+    }
+    hanging; /**< if \a is_hanging = 1,
+                  use is.hanging to access per-quadrant data */
+  }
+  is;
+}
+p8est_iter_face_side_t;
+
+/** The information that is available to the user-defined p8est_iter_face_t
+ * callback.
+ *
+ * The orientation is 0 if the face is within one tree; otherwise, it is the
+ * same as the orientation value between the two trees given in the
+ * connectivity.  If the face is on the outside of the forest, then there is
+ * only one side.  If tree_boundary is false, the face is on the interior of a
+ * tree.  When tree_boundary false, sides[0] contains the lowest z-order
+ * quadrant that touches the face.
+ * When tree_boundary is true, its value is P8EST_CONNECT_FACE.
+ */
+typedef struct p8est_iter_face_info
+{
+  p8est_t            *p4est;
+  p8est_ghost_t      *ghost_layer;
+  int8_t              orientation; /**< the orientation of the sides to each
+                                        other, as in the definition of
+                                        p8est_connectivity_t */
+  int8_t              tree_boundary; /**< boolean: interior face (0),
+                                          boundary face (1) */
+  sc_array_t          sides;    /* array of p8est_iter_face_side_t type */
+}
+p8est_iter_face_info_t;
+
+/** The prototype for a function that p8est_iterate() will execute wherever
+ * two quadrants share a face: the face can be a 2:1 hanging face, it does not
+ * have to be conformal.
+ *
+ * \param [in] info          information about a quadrant provided to the user
+ * \param [in,out] user_data the user context passed to p8est_iterate()
+ *
+ * \note the forest must be face balanced for p8est_iterate() to execute a
+ * callback function on faces (see p8est_balance()).
+ */
+typedef void        (*p8est_iter_face_t) (p8est_iter_face_info_t * info,
+                                          void *user_data);
+
+/* The information that is available to the user-defined p8est_iter_edge_t
+ * callback.
+ *
+ * If a \a quad is local (\a is_ghost is false), then its \a quadid indexes
+ * the tree's quadrant array; otherwise, it indexes the ghosts array. If the
+ * edge is hanging, then the quadrants are listed in z-order. If an edge is in
+ * the interior of a tree, orientation is 0; if an edge is between trees,
+ * orientation is the same as edge orientation in the connectivity. If a
+ * quadrant should be present, but it is not included in the ghost layer, then
+ * quad = NULL, is_ghost is true, and quadid = -1.
+                      *
+ * the \a faces field provides some additional information about the local
+ * neighborhood: if side[i]->faces[j] == side[k]->faces[l], this indicates that
+ * there is a common face between these two sides of the edge.
+ */
+typedef struct p8est_iter_edge_side
+{
+  p4est_topidx_t      treeid;          /**< the tree on this side */
+  int8_t              edge;            /**< which quadrant side the edge
+                                            touches */
+  int8_t              orientation; /**< the orientation of each quadrant
+                                        relative to this edge, as in the
+                                        definition of p8est_connectivity_t */
+
+  int8_t              is_hanging;      /**< boolean: one full quad (0) or
+                                            two smaller quads (1) */
+  union p8est_iter_edge_side_data
+  {
+    struct
+    {
+      int8_t              is_ghost;    /**< boolean: local (0) or ghost (1) */
+      p8est_quadrant_t   *quad;        /**< the actual quadrant */
+      p4est_locidx_t      quadid;      /**< index in tree or ghost array */
+    }
+    full; /**< if \a is_hanging = 0,
+               use is.full to access per-quadrant data */
+
+    struct
+    {
+      int8_t              is_ghost[2]; /**< boolean: local (0) or ghost (1) */
+      p8est_quadrant_t   *quad[2];     /**< the actual quadrant */
+      p4est_locidx_t      quadid[2];   /**< index in tree or ghost array */
+    }
+    hanging; /**< if \a is_hanging = 1,
+                  use is.hanging to access per-quadrant data */
+  }
+  is;
+  int8_t              faces[2];
+}
+p8est_iter_edge_side_t;
+
+/** The information about all sides of an edge in the forest.
+ * If tree_boundary is false, the edge is on the interior of a tree.
+ * When tree_boundary is false, sides[0] contains the lowest z-order quadrant
+ * that touches the edge.
+ * When tree_boundary is true, its value is P8EST_CONNECT_FACE/EDGE
+ * depending on the location of the edge relative to the tree.
+ */
+typedef struct p8est_iter_edge_info
+{
+  p8est_t            *p4est;
+  p8est_ghost_t      *ghost_layer;
+  int8_t              tree_boundary;  /**< boolean: interior face (0),
+                                           boundary face (1) */
+  sc_array_t          sides; /**< array of p8est_iter_edge_side_t type */
+}
+p8est_iter_edge_info_t;
+
+/** The prototype for a function that p8est_iterate will execute wherever
+ * the edge is an edge of all quadrants that touch it i.e. the callback will
+ * not execute on an edge the sits on a hanging face.
+ *
+ * \param [in] info          information about a quadrant provided to the user
+ * \param [in,out] user_data the user context passed to p8est_iterate()
+ *
+ * \note the forest must be edge balanced for p8est_iterate() to execute a
+ * callback function on edges.
+ */
+typedef void        (*p8est_iter_edge_t) (p8est_iter_edge_info_t * info,
+                                          void *user_data);
+
+/* Information about one side of a corner in the forest.  If a \a quad is local,
+ * then its \a quadid indexes the tree's quadrant array; otherwise, it indexes
+ * the ghosts array.
+ *
+ * the \a faces and \a edges field provides some additional information about
+ * the local neighborhood: if side[i]->faces[j] == side[k]->faces[l], this
+ * indicates that there is a common face between these two sides of the
+ * corner.
+ */
+typedef struct p8est_iter_corner_side
+{
+  p4est_topidx_t      treeid;   /**< the tree that contains \a quad */
+  int8_t              corner;   /**< which of the quadrant's corners touches
+                                     this corner */
+  int8_t              is_ghost; /**< boolean: local (0) or ghost (1) */
+  p8est_quadrant_t   *quad;
+  p4est_locidx_t      quadid;   /**< the index in the tree or ghost array */
+  int8_t              faces[3]; /**< internal work data */
+  int8_t              edges[3]; /**< internal work data */
+}
+p8est_iter_corner_side_t;
+
+/** The information that is availalbe to the user-defined p8est_iter_corner_t
+ * callback.
+ *
+ * If tree_boundary is false, the corner is on the interior of a tree.
+ * When tree_boundary is false, sides[0] contains the lowest z-order quadrant
+ * that touches the corner.
+ * When tree_boundary is true, its value is P8EST_CONNECT_FACE/EDGE/CORNER
+ * depending on the location of the corner relative to the tree.
+ */
+typedef struct p8est_iter_corner_info
+{
+  p8est_t            *p4est;
+  p8est_ghost_t      *ghost_layer;
+  int8_t              tree_boundary; /**< boolean: interior face (0),
+                                           boundary face (1) */
+  sc_array_t          sides; /**< array of p8est_iter_corner_side_t type */
+}
+p8est_iter_corner_info_t;
+
+/** The prototype for a function that p8est_iterate will execute wherever
+ * the corner is a corner for all quadrants that touch it
+ *
+ * i.e. the callback will not execute on a corner that sits on a hanging face
+ * or edge.
+ *
+ * \param [in] info          information about a quadrant provided to the user
+ * \param [in,out] user_data the user context passed to p8est_iterate()
+ *
+ * \note the forest does not need to be corner balanced for p8est_iterate() to
+ * execute a callback function at corners, only face and edge balanced.
+ */
+typedef void        (*p8est_iter_corner_t) (p8est_iter_corner_info_t * info,
+                                            void *user_data);
+
+/** Execute the user-supplied callback functions at every volume, face, edge
+ * and corner in the local forest.
+ *
+ * The ghost_layer may be NULL. The \a user_data pointer is not touched by
+ * p8est_iterate, but is passed to each of the callbacks. Any of the callback
+ * functions may be NULL.  The callback functions are interspersed with each
+ * other, i.e. some face callbacks will occur between volume callbacks, and
+ * some edge callbacks will occur between face callbacks, etc.:
+ *
+ * 1) volume callbacks occur in the sorted Morton-index order.
+ * 2) a face callback is not executed until after the volume callbacks have
+ *    been executed for the quadrants that share it.
+ * 3) an edge callback is not executed until the face callbacks have been
+ *    executed for all faces that touch the edge.
+ * 4) a corner callback is not executed until the edge callbacks have been
+ *    executed for all edges that touch the corner.
+ * 5) it is not always the case that every face callback for a given quadrant
+ *    is executed before any of the edge or corner callbacks, and it is not
+ *    always the case that every edge callback for a given quadrant is executed
+ *    before any of the corner callbacks.
+ * 6) callbacks are not executed at faces, edges or corners that only involve
+ *    ghost quadrants, i.e. that are not adjacent in the local section of the
+ *    forest.
+ *
+ * \param[in] p4est          the forest
+ * \param[in] ghost_layer    optional: when not given, callbacks at the
+ *                           boundaries of the local partition cannot provide
+ *                           quadrant data about ghost quadrants: missing
+ *                           (p8est_quadrant_t *) pointers are set to NULL,
+ *                           missing indices are set to -1.
+ * \param[in,out] user_data  optional context to supply to each callback
+ * \param[in] iter_volume    callback function for every quadrant's interior
+ * \param[in] iter_face      callback function for every face between
+ *                           quadrants
+ * \param[in] iter_edge      callback function for every edge between
+ *                           quadrants
+ * \param[in] iter_corner    callback function for every corner between
+ *                           quadrants
+ */
+void                p8est_iterate (p8est_t * p4est,
+                                   p8est_ghost_t * ghost_layer,
+                                   void *user_data,
+                                   p8est_iter_volume_t iter_volume,
+                                   p8est_iter_face_t iter_face,
+                                   p8est_iter_edge_t iter_edge,
+                                   p8est_iter_corner_t iter_corner);
+
+/** Return a pointer to a iter_corner_side array element indexed by a int.
+ */
+/*@unused@*/
+static inline p8est_iter_corner_side_t *
+p8est_iter_cside_array_index_int (sc_array_t * array, int it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_iter_corner_side_t));
+  P4EST_ASSERT (it >= 0 && (size_t) it < array->elem_count);
+
+  return (p8est_iter_corner_side_t *)
+    (array->array + sizeof (p8est_iter_corner_side_t) * it);
+}
+
+/** Return a pointer to a iter_corner_side array element indexed by a size_t.
+ */
+/*@unused@*/
+static inline p8est_iter_corner_side_t *
+p8est_iter_cside_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_iter_corner_side_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p8est_iter_corner_side_t *)
+    (array->array + sizeof (p8est_iter_corner_side_t) * it);
+}
+
+/** Return a pointer to a iter_edge_side array element indexed by a int.
+ */
+/*@unused@*/
+static inline p8est_iter_edge_side_t *
+p8est_iter_eside_array_index_int (sc_array_t * array, int it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_iter_edge_side_t));
+  P4EST_ASSERT (it >= 0 && (size_t) it < array->elem_count);
+
+  return (p8est_iter_edge_side_t *)
+    (array->array + sizeof (p8est_iter_edge_side_t) * it);
+}
+
+/** Return a pointer to a iter_edge_side array element indexed by a size_t.
+ */
+/*@unused@*/
+static inline p8est_iter_edge_side_t *
+p8est_iter_eside_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_iter_edge_side_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p8est_iter_edge_side_t *)
+    (array->array + sizeof (p8est_iter_edge_side_t) * it);
+}
+
+/** Return a pointer to a iter_face_side array element indexed by a int.
+ */
+/*@unused@*/
+static inline p8est_iter_face_side_t *
+p8est_iter_fside_array_index_int (sc_array_t * array, int it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_iter_face_side_t));
+  P4EST_ASSERT (it >= 0 && (size_t) it < array->elem_count);
+
+  return (p8est_iter_face_side_t *)
+    (array->array + sizeof (p8est_iter_face_side_t) * it);
+}
+
+/** Return a pointer to a iter_face_side array element indexed by a size_t.
+ */
+/*@unused@*/
+static inline p8est_iter_face_side_t *
+p8est_iter_fside_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_iter_face_side_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p8est_iter_face_side_t *)
+    (array->array + sizeof (p8est_iter_face_side_t) * it);
+}
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_ITERATE_H */
diff --git a/src/p8est_lnodes.c b/src/p8est_lnodes.c
new file mode 100644
index 0000000..3c7d950
--- /dev/null
+++ b/src/p8est_lnodes.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_lnodes.c"
diff --git a/src/p8est_lnodes.h b/src/p8est_lnodes.h
new file mode 100644
index 0000000..72eb592
--- /dev/null
+++ b/src/p8est_lnodes.h
@@ -0,0 +1,404 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P8EST_LNODES_H
+#define P8EST_LNODES_H
+
+#include <p8est.h>
+#include <p8est_ghost.h>
+
+SC_EXTERN_C_BEGIN;
+
+typedef int16_t     p8est_lnodes_code_t;
+
+/** Store a parallel numbering of Lobatto points of a given degree > 0.
+ *
+ * Each element has degree+1 nodes per edge
+ * and vnodes = (degree+1)^3 nodes per volume.
+ * element_nodes is of dimension vnodes * num_local_elements and lists the
+ * nodes of each element in lexicographic yx-order (x varies fastest);
+ * element_nodes indexes into the set of local nodes, layed out as follows:
+ * local nodes = [<-----owned_count----->|<-----nonlocal_nodes----->]
+ *             = [<----------------num_local_nodes----------------->]
+ * nonlocal_nodes contains the globally unique numbers for independent nodes
+ * that are owned by other processes; for local nodes, the globally unique
+ * numbers are given by i + global_offset, where i is the local number.
+ * Hanging nodes are always local and don't have a global number.
+ * They index the geometrically corresponding independent nodes of a neighbor.
+ *
+ * Whether nodes are hanging or not is decided based on the element faces and
+ * edges. This information is encoded in face_code with one int16_t per
+ * element. If no faces or edges are hanging, the value is zero, otherwise the
+ * face_code is interpreted by p8est_lnodes_decode.
+ *
+ * Independent nodes can be shared by multiple MPI ranks.
+ * The owner rank of a node is the one from the lowest numbered element
+ * on the lowest numbered octree *touching* the node.
+ *
+ * What is meant by *touching*?
+ * A quadrant is said to touch all faces/edges/corners that are incident on it,
+ * and by extension all nodes that are contained in those faces/edges/corners.
+ *
+ *            X      +-----------+
+ *             x     |\           \
+ *            x      | \           \
+ *             . x   |  \           \
+ *            x   X  |   +-----------+
+ * +-----+     . .   |   |           |
+ * |\     \   X   o  +   |           |
+ * | +-----+   o .    \  |     p     |
+ * + |  q  |      o    \ |           |
+ *  \|     |     o      \|           |
+ *   +-----+      O      +-----------+
+ *
+ * In this example degree = 3.  There are 4 nodes that live on the face
+ * between q and p, two on each edge and one at each corner of that face.
+ * The face is incident on q, so q owns the nodes marked '.' on the face
+ * (provided q is from a lower tree or has a lower index than p).
+ * The bottom and front edges are incident on q, so q owns its nodes marked
+ * 'o' as well.
+ * The front lower corner is incident on q, so q owns its node 'O' as
+ * well.  The other edges and corners are not incident on q, so q cannot own
+ * their nodes, marked 'x' and 'X'.
+ *
+ * global_owned_count contains the number of independent nodes owned by each
+ * process.
+ *
+ * The sharers array contains items of type p8est_lnodes_rank_t
+ * that hold the ranks that own or share independent local nodes.
+ * If there are no shared nodes on this processor, it is empty.
+ * Otherwise, it is sorted by rank and the current process is included.
+ *
+ * degree < 0 indicates that the lnodes data structure is being used to number
+ * the quadrant boundary object (faces, edge  and corners) rather than the
+ * $C^0$ Lobatto nodes:
+ *
+ * if degree == -1, then one node is assigned per face, and no nodes are
+ * assigned per volume, per edge,  or per corner: this numbering can be used
+ * for low-order Raviart-Thomas elements.  In this case, vnodes == 6, and the
+ * nodes are listed in face-order.
+ *
+ * if degree == -2, then one node is assigned per face and per edge and no
+ * nodes are assigned per volume or per corner.  In this case, vnodes == 18,
+ * and the nodes are listed in face-order, followed by edge-order.
+ *
+ * if degree == -3, then one node is assigned per face, per edge and per
+ * corner and no nodes are assigned per volume.  In this case, vnodes == 26,
+ * and the nodes are listed in face-order, followed by edge-order, followed by
+ * corner-order.
+ *
+ */
+typedef struct p8est_lnodes
+{
+  sc_MPI_Comm         mpicomm;
+  p4est_locidx_t      num_local_nodes;
+  p4est_locidx_t      owned_count;
+  p4est_gloidx_t      global_offset;
+  p4est_gloidx_t     *nonlocal_nodes;
+  sc_array_t         *sharers;
+  p4est_locidx_t     *global_owned_count;
+
+  int                 degree, vnodes;
+  p4est_locidx_t      num_local_elements;
+  p8est_lnodes_code_t *face_code;
+  p4est_locidx_t     *element_nodes;
+}
+p8est_lnodes_t;
+
+/** The structure stored in the sharers array.
+ *
+ * shared_nodes is a sorted array of p4est_locidx_t
+ * that indexes into local nodes.  The shared_nodes array has a
+ * contiguous (or empty) section of nodes owned by the current rank.
+ * shared_mine_offset and shared_mine_count identify this section
+ * by indexing the shared_nodes array, not the local nodes array.
+ * owned_offset and owned_count define the section of local nodes
+ * that is owned by the listed rank (the section may be empty).
+ * For the current process these coincide with those in p8est_lnodes_t.
+ */
+typedef struct p8est_lnodes_rank
+{
+  int                 rank;
+  sc_array_t          shared_nodes;
+  p4est_locidx_t      shared_mine_offset, shared_mine_count;
+  p4est_locidx_t      owned_offset, owned_count;
+}
+p8est_lnodes_rank_t;
+
+/** Decode the face_code into hanging face information.
+ *
+ * This is mostly for demonstration purposes.  Applications probably will
+ * integrate it into their own loop over the face for performance reasons.
+ *
+ * \param[in] face_code as in the p8est_lnodes_t structure.
+ * \param[out] hanging_face: if there are hanging faces or edges,
+ *             hanging_face = -1 if the face is not hanging,
+ *                          = the corner of the full face that it touches:
+ *                            e.g. if face = i and hanging_face[i] =
+ *                            j, then the interpolation operator corresponding
+ *                            to corner j should be used for that face.
+ *             note: not touched if there are no hanging faces or edges.
+ * \param[out] hanging_edge: if there are hanging faces or edges,
+ *             hanging_edge = -1 if the edge is not hanging,
+ *                          =  0 if the edge is the first half of a full edge,
+ *                               but neither of the two faces touching the
+ *                               edge is hanging,
+ *                          =  1 if the edge is the second half of a full edge,
+ *                               but neither of the two faces touching the
+ *                               edge is hanging,
+ *                          =  2 if the edge is the first half of a full edge
+ *                               and is on the boundary of a full face,
+ *                          =  3 if the edge is the second half of a full edge
+ *                               and is on the boundary of a full face,
+ *                          =  4 if the edge is in the middle of a full face.
+ *                               See the diagram below for clarification.
+ *             note: not touched if there are no hanging faces or edges.
+ * \return             true if any face or edge is hanging, false otherwise.
+ *
+ * o...............o  o...............o  +---2---+.......o  o.......+---3---+
+ * :               :  :               :  |       |       :  :       |       |
+ * :               :  :               :  3   2   4       :  :       4   3   3
+ * :               :  :               :  |       |       :  :       |       |
+ * +---4---+       :  :       +---4---+  +---4---+       :  :       +---4---+
+ * |       |       :  :       |       |  :               :  :               :
+ * 2   0   4       :  :       4   1   2  :               :  :               :
+ * |       |       :  :       |       |  :               :  :               :
+ * +---2---+.......o  o.......+---3---+  o...............o  o...............o
+ *
+ *                    o                  +-------+
+ *                    :                  |\       \
+ *                    :                  1 \       \
+ *                    :                  |  +-------+
+ *                    +-------+          +  |       |
+ *                    |\       \         :\ |       |
+ *                    0 \       \        : \|       |
+ *                    |  +-------+       :  +-------+
+ *                    +  |       |       o
+ *                     \ |       |
+ *                      \|       |
+ *                       +-------+
+ */
+/*@unused@*/
+static inline int
+p8est_lnodes_decode (p8est_lnodes_code_t face_code, int hanging_face[6],
+                     int hanging_edge[12])
+{
+  P4EST_ASSERT (face_code >= 0);
+
+  if (face_code) {
+    int                 i, j;
+    int16_t             c = face_code & 0x0007;
+    int16_t             cwork;
+    int                 f;
+    int                 e;
+    int16_t             work = face_code >> 3;
+
+    memset (hanging_face, -1, 6 * sizeof (int));
+    memset (hanging_edge, -1, 12 * sizeof (int));
+
+    cwork = c;
+    for (i = 0; i < 3; ++i) {
+      if (work & 0x0001) {
+        f = p8est_corner_faces[c][i];
+        hanging_face[f] = p8est_corner_face_corners[c][f];
+        for (j = 0; j < 4; j++) {
+          e = p8est_face_edges[f][j];
+          hanging_edge[e] = 4;
+        }
+      }
+      work >>= 1;
+    }
+    for (i = 0; i < 3; ++i) {
+      if (work & 0x0001) {
+        e = p8est_corner_edges[c][i];
+        hanging_edge[e] = (hanging_edge[e] == -1) ? 0 : 2;
+        hanging_edge[e] += (int) (cwork & 0x0001);
+      }
+      cwork >>= 1;
+      work >>= 1;
+    }
+    return 1;
+  }
+  else {
+    return 0;
+  }
+}
+
+p8est_lnodes_t     *p8est_lnodes_new (p8est_t * p8est,
+                                      p8est_ghost_t * ghost_layer,
+                                      int degree);
+
+void                p8est_lnodes_destroy (p8est_lnodes_t *);
+
+/** Expand the ghost layer to include the support of all nodes supported on
+ * the local partition.
+ *
+ * \param [in]     p8est        The forest from which the ghost layer was
+ *                              generated.
+ * \param [in]     lnodes       The nodes to support.
+ * \param [in,out] ghost        The ghost layer to be expanded.
+ */
+void                p8est_ghost_support_lnodes (p8est_t * p8est,
+                                                p8est_lnodes_t * lnodes,
+                                                p8est_ghost_t * ghost);
+
+/** Expand the ghost layer as in p8est_ghost_expand(), but use node support to
+ * define adjacency instead of geometric adjacency.
+ *
+ * \param [in]     p8est        The forest from which the ghost layer was
+ *                              generated.
+ * \param [in]     lnodes       The nodes to support.
+ * \param [in,out] ghost        The ghost layer to be expanded.
+ */
+void                p8est_ghost_expand_by_lnodes (p8est_t * p4est,
+                                                  p8est_lnodes_t * lnodes,
+                                                  p8est_ghost_t * ghost);
+
+/** p8est_lnodes_buffer_t handles the communication of data associated with
+ * nodes.
+ *
+ * \a send_buffers is an array of arrays: one buffer for each process to which
+ * the current process sends node-data.  It should not be altered between
+ * a shared_*_begin and a shared_*_end call.
+ *
+ * \a recv_buffers is an array of arrays that is used in lnodes_share_all_*.
+ * \a recv_buffers[j] corresponds with lnodes->sharers[j]: it is the same
+ * length as \a lnodes->sharers[j]->shared_nodes.  At the completion of
+ * lnodes_share_all or lnodes_share_all_end, recv_buffers[j] contains the
+ * node-data from the process lnodes->sharers[j]->rank
+ * (unless j is the current rank, in which case recv_buffers[j] is empty).
+ */
+typedef struct p8est_lnodes_buffer
+{
+  sc_array_t         *requests; /* sc_MPI_Request */
+  sc_array_t         *send_buffers;
+  sc_array_t         *recv_buffers;
+}
+p8est_lnodes_buffer_t;
+
+/** p8est_lnodes_share_owned_begin
+ *
+ * \a node_data is a user-defined array of arbitrary type, where each entry
+ * is associated with the \a lnodes local nodes entry of matching index.
+ * For every local nodes entry that is owned by a process
+ * other than the current one, the value in the \a node_data array of the
+ * owning process is written directly into the \a node_data array of the current
+ * process.  Values of \a node_data are not guaranteed to be sent or received
+ * until the \a buffer created by p8est_lnodes_share_owned_begin is passed to
+ * p8est_lnodes_share_owned_end.
+ *
+ * To be memory neutral, the \a buffer created by
+ * p8est_lnodes_share_owned_begin must be destroying with
+ * p8est_lnodes_buffer_destroy (it is not destroyed by
+ * p8est_lnodes_share_owned_end).
+ */
+p8est_lnodes_buffer_t *p8est_lnodes_share_owned_begin (sc_array_t * node_data,
+                                                       p8est_lnodes_t *
+                                                       lnodes);
+
+void                p8est_lnodes_share_owned_end (p8est_lnodes_buffer_t *
+                                                  buffer);
+
+/** Equivalent to calling p8est_lnodes_share_owned_end directly after
+ * p8est_lnodes_share_owned_begin.  Use if there is no local work that can be
+ * done to mask the communication cost.
+ */
+void                p8est_lnodes_share_owned (sc_array_t * node_data,
+                                              p8est_lnodes_t * lnodes);
+
+/** p8est_lnodes_share_all_begin
+ *
+ * \a node_data is a user_defined array of arbitrary type, where each entry
+ * is associated with the \a lnodes local nodes entry of matching index.
+ * For every process that shares an entry with the current one, the value in
+ * the \a node_data array of that process is written into a
+ * \a buffer->recv_buffers entry as described above.  The user can then perform
+ * some arbitrary work that requires the data from all processes that share a
+ * node (such as reduce, max, min, etc.).  When the work concludes, the
+ * \a buffer should be destroyed with p8est_lnodes_buffer_destroy.
+ *
+ * Values of \a node_data are not guaranteed to be send, and
+ * \a buffer->recv_buffer entries are not guaranteed to be received until
+ * the \a buffer created by p8est_lnodes_share_all_begin is passed to
+ * p8est_lnodes_share_all_end.
+ */
+p8est_lnodes_buffer_t *p8est_lnodes_share_all_begin (sc_array_t * node_data,
+                                                     p8est_lnodes_t * lnodes);
+
+void                p8est_lnodes_share_all_end (p8est_lnodes_buffer_t *
+                                                buffer);
+
+/** Equivalend to calling p8est_lnodes_share_all_end directly after
+ * p8est_lnodes_share_all_begin.  Use if there is no local work that can be
+ * done to mask the communication cost.
+ * \return          A fully initialized buffer that contains the received data.
+ *                  After processing this data, the buffer must be freed with
+ *                  p8est_lnodes_buffer_destroy.
+ */
+p8est_lnodes_buffer_t *p8est_lnodes_share_all (sc_array_t * node_data,
+                                               p8est_lnodes_t * lnodes);
+
+void                p8est_lnodes_buffer_destroy (p8est_lnodes_buffer_t *
+                                                 buffer);
+
+/** Return a pointer to a lnodes_rank array element indexed by a int.
+ */
+/*@unused@*/
+static inline p8est_lnodes_rank_t *
+p8est_lnodes_rank_array_index_int (sc_array_t * array, int it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_lnodes_rank_t));
+  P4EST_ASSERT (it >= 0 && (size_t) it < array->elem_count);
+
+  return (p8est_lnodes_rank_t *)
+    (array->array + sizeof (p8est_lnodes_rank_t) * it);
+}
+
+/** Return a pointer to a lnodes_rank array element indexed by a size_t.
+ */
+/*@unused@*/
+static inline p8est_lnodes_rank_t *
+p8est_lnodes_rank_array_index (sc_array_t * array, size_t it)
+{
+  P4EST_ASSERT (array->elem_size == sizeof (p8est_lnodes_rank_t));
+  P4EST_ASSERT (it < array->elem_count);
+
+  return (p8est_lnodes_rank_t *)
+    (array->array + sizeof (p8est_lnodes_rank_t) * it);
+}
+
+/** Compute the global number of a local node number */
+/*@unused@*/
+static inline       p4est_gloidx_t
+p8est_lnodes_global_index (p8est_lnodes_t * lnodes, p4est_locidx_t lidx)
+{
+  p4est_locidx_t      owned = lnodes->owned_count;
+  P4EST_ASSERT (lidx >= 0 && lidx < lnodes->num_local_nodes);
+
+  return (lidx < owned) ? lnodes->global_offset + lidx :
+    lnodes->nonlocal_nodes[lidx - owned];
+}
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_LNODES */
diff --git a/src/p8est_mesh.c b/src/p8est_mesh.c
new file mode 100644
index 0000000..92be501
--- /dev/null
+++ b/src/p8est_mesh.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_mesh.c"
diff --git a/src/p8est_mesh.h b/src/p8est_mesh.h
new file mode 100644
index 0000000..16ab7d4
--- /dev/null
+++ b/src/p8est_mesh.h
@@ -0,0 +1,229 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_mesh.h
+ *
+ * forest topology in a conventional mesh format
+ *
+ * \ingroup p8est
+ */
+
+#ifndef P8EST_MESH_H
+#define P8EST_MESH_H
+
+#include <p8est_ghost.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** This structure contains complete mesh information on the forest.
+ * It stores the locally relevant neighborhood, that is, all locally owned
+ * quadrants and one layer of adjacent ghost quadrants and their owners.
+ *
+ * For each local quadrant, its tree number is stored in quad_to_tree. The
+ * quad_to_tree array is NULL by default and can be enabled using
+ * p8est_mesh_new_ext.
+ * For each ghost quadrant, its owner rank is stored in ghost_to_proc.
+ * For each level, an array of local quadrant numbers is stored in quad_level.
+ * The quad_level array is NULL by default and can be enabled using
+ * p8est_mesh_new_ext.
+ *
+ * The quad_to_quad list stores one value for each local quadrant's face.
+ * This value is in 0..local_num_quadrants-1 for local quadrants, or in
+ * local_num_quadrants + (0..ghost_num_quadrants-1) for ghost quadrants.
+ * The quad_to_face list has equally many entries which are either:
+ * 1. A value of v = 0..23 indicates one same-size neighbor.
+ *    This value is decoded as v = r * 6 + nf, where nf = 0..5 is the
+ *    neighbor's connecting face number and r = 0..3 is the relative
+ *    orientation of the neighbor's face, see p8est_connectivity.h.
+ * 2. A value of v = 24..119 indicates a double-size neighbor.
+ *    This value is decoded as v = 24 + h * 24 + r * 6 + nf, where
+ *    r and nf are as above and h = 0..3 is the number of the subface.
+ * 3. A value of v = -24..-1 indicates four half-size neighbors.
+ *    In this case the corresponding quad_to_quad index points into the
+ *    quad_to_half array which stores four quadrant numbers per index,
+ *    and the orientation of the smaller faces follows from 24 + v.
+ *    The entries of quad_to_half encode between local and ghost quadrant
+ *    in the same way as the quad_to_quad values described above.
+ * A quadrant on the boundary of the forest sees itself and its face number.
+ *
+ * The quad_to_corner list stores corner neighbors that are not face or edge
+ * neighbors.  On the inside of a tree, there is precisely one such neighbor
+ * per corner.  In this case, its index is encoded as described above for
+ * quad_to_quad.  The neighbor's matching corner number is always diagonally
+ * opposite.
+ *
+ * On the inside of an inter-tree face, we have precisely one corner neighbor.
+ * If a corner is across an inter-tree edge or corner, then the number of
+ * corner neighbors may be any non-negative number.  In all inter-tree cases,
+ * the quad_to_corner value is in
+ *    local_num_quadrants + local_num_ghosts + [0 .. local_num_corners - 1]
+ * where the offset by local quadrants and ghosts is implicitly substracted.
+ * It indexes into corner_offset, which encodes a group of corner neighbors.
+ * Each group contains the quadrant numbers encoded as usual for quad_to_quad
+ * in corner_quad, and the corner number from the neighbor as corner_corner.
+ *
+ * Intra-tree corners and corners across an inter-tree face are implemented.
+ * Other inter-tree corners are NOT IMPLEMENTED and are assigned the value -2.
+ * Corners with no diagonal neighbor at all are assigned the value -1.
+ */
+typedef struct
+{
+  p4est_locidx_t      local_num_quadrants;
+  p4est_locidx_t      ghost_num_quadrants;
+
+  p4est_topidx_t     *quad_to_tree;     /**< tree index for each local quad,
+                                             NULL by default */
+  int                *ghost_to_proc;    /**< processor for each ghost quad */
+
+  p4est_locidx_t     *quad_to_quad;     /**< one index for each of the 6 faces */
+  int8_t             *quad_to_face;     /**< encodes orientation/2:1 status */
+  sc_array_t         *quad_to_half;     /**< stores half-size neighbors */
+  sc_array_t         *quad_level;       /**< stores lists of per-level quads,
+                                             NULL by default */
+
+  /* These members are NULL if the connect_t is not P4EST_CONNECT_CORNER */
+  /* CAUTION: tree-boundary corners not yet implemented */
+  p4est_locidx_t      local_num_corners;        /* tree-boundary corners */
+  p4est_locidx_t     *quad_to_corner;   /* 8 indices for each local quad */
+  sc_array_t         *corner_offset;    /* local_num_corners + 1 entries */
+  sc_array_t         *corner_quad;      /* corner_offset indexes into this */
+  sc_array_t         *corner_corner;    /* and this one too (type int8_t) */
+}
+p8est_mesh_t;
+
+/** This structure can be used as the status of a face neighbor iterator.
+  * It always contains the face and subface of the neighbor to be processed.
+  */
+typedef struct
+{
+  /* forest information */
+  p8est_t            *p4est;
+  p8est_ghost_t      *ghost;
+  p8est_mesh_t       *mesh;
+
+  /* quadrant information */
+  p4est_topidx_t      which_tree;
+  p4est_locidx_t      quadrant_id;      /* tree-local quadrant index */
+  p4est_locidx_t      quadrant_code;    /* 6 * (quadrant_id + tree_offset) */
+
+  /* neighbor information */
+  int                 face;     /* Face number in 0..5. */
+  int                 subface;  /* Hanging neighbor number in 0..3. */
+
+  /* internal information */
+  p4est_locidx_t      current_qtq;
+}
+p8est_mesh_face_neighbor_t;
+
+/** Calculate the memory usage of the mesh structure.
+ * \param [in] mesh     Mesh structure.
+ * \return              Memory used in bytes.
+ */
+size_t              p8est_mesh_memory_used (p8est_mesh_t * mesh);
+
+/** Create a p8est_mesh structure.
+ * \param [in] p8est    A forest that is fully 2:1 balanced.
+ * \param [in] ghost    The ghost layer created from the provided p4est.
+ * \param [in] btype    Determines the highest codimension of neighbors.
+ * \return              A fully allocated mesh structure.
+ */
+p8est_mesh_t       *p8est_mesh_new (p8est_t * p8est, p8est_ghost_t * ghost,
+                                    p8est_connect_type_t btype);
+
+/** Destroy a p8est_mesh structure.
+ * \param [in] mesh     Mesh structure previously created by p8est_mesh_new.
+ */
+void                p8est_mesh_destroy (p8est_mesh_t * mesh);
+
+/** Find a quadrant based on its cumulative number in the local forest.
+ * \param [in]  p8est           Forest to be worked with.
+ * \param [in]  cumulative_id   Cumulative index over all trees of quadrant.
+ * \param [in,out] which_tree   If not NULL, the input value can be -1
+ *                              or an initial guess for the quadrant's tree
+ *                              and output is the tree of returned quadrant.
+ * \param [out] quadrant_id     If not NULL, the number of quadrant in tree.
+ * \return                      The identified quadrant.
+ */
+p8est_quadrant_t   *p8est_mesh_quadrant_cumulative (p8est_t * p8est,
+                                                    p4est_locidx_t
+                                                    cumulative_id,
+                                                    p4est_topidx_t
+                                                    * which_tree,
+                                                    p4est_locidx_t
+                                                    * quadrant_id);
+
+/** Initialize a mesh neighbor iterator by quadrant index.
+ * \param [out] mfn         A p8est_mesh_face_neighbor_t to be initialized.
+ * \param [in]  which_tree  Tree of quadrant whose neighbors are looped over.
+ * \param [in]  quadrant_id Index relative to which_tree of quadrant.
+ */
+void                p8est_mesh_face_neighbor_init2 (p8est_mesh_face_neighbor_t
+                                                    * mfn, p8est_t * p8est,
+                                                    p8est_ghost_t * ghost,
+                                                    p8est_mesh_t * mesh,
+                                                    p4est_topidx_t which_tree,
+                                                    p4est_locidx_t
+                                                    quadrant_id);
+
+/** Initialize a mesh neighbor iterator by quadrant pointer.
+ * \param [out] mfn         A p8est_mesh_face_neighbor_t to be initialized.
+ * \param [in]  which_tree  Tree of quadrant whose neighbors are looped over.
+ * \param [in]  quadrant    Pointer to quadrant contained in which_tree.
+ */
+void                p8est_mesh_face_neighbor_init (p8est_mesh_face_neighbor_t
+                                                   * mfn, p8est_t * p8est,
+                                                   p8est_ghost_t * ghost,
+                                                   p8est_mesh_t * mesh,
+                                                   p4est_topidx_t which_tree,
+                                                   p8est_quadrant_t
+                                                   * quadrant);
+
+/** Move the iterator forward to loop around neighbors of the quadrant.
+ * \param [in,out] mfn      Internal status of the iterator.
+ * \param [out]    ntree    If not NULL, the tree number of the neighbor.
+ * \param [out]    nquad    If not NULL, the quadrant number within tree.
+ *                          For ghosts instead the number in ghost layer.
+ * \param [out]    nface    If not NULL, neighbor's face as in p8est_mesh_t.
+ * \param [out]    nrank    If not NULL, the owner process of the neighbor.
+ * \return                  Either a real quadrant or one from the ghost layer.
+ *                          Returns NULL when the iterator is done.
+ */
+p8est_quadrant_t   *p8est_mesh_face_neighbor_next (p8est_mesh_face_neighbor_t
+                                                   * mfn,
+                                                   p4est_topidx_t * ntree,
+                                                   p4est_locidx_t * nquad,
+                                                   int *nface, int *nrank);
+
+/** Get the user data for the current face neighbor.
+ * \param [in]     mfn           Internal status of the iterator.
+ * \param [in]     ghost_data    Data for the ghost quadrants that has been
+ *                               synchronized with p4est_ghost_exchange_data.
+ * \return                       A pointer to the user data for the current
+ *                               neighbor.
+ */
+void               *p8est_mesh_face_neighbor_data (p8est_mesh_face_neighbor_t
+                                                   * mfn, void *ghost_data);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_MESH_H */
diff --git a/src/p8est_nodes.c b/src/p8est_nodes.c
new file mode 100644
index 0000000..2c6f748
--- /dev/null
+++ b/src/p8est_nodes.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_nodes.c"
diff --git a/src/p8est_nodes.h b/src/p8est_nodes.h
new file mode 100644
index 0000000..27d7aea
--- /dev/null
+++ b/src/p8est_nodes.h
@@ -0,0 +1,219 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P8EST_NODES_H
+#define P8EST_NODES_H
+
+#include <p8est.h>
+#include <p8est_ghost.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Store an independent node.
+ * Keep this in sync with the p8est_t data structure.
+ */
+typedef struct p8est_indep
+{
+  p4est_qcoord_t      x, y, z;
+  int8_t              level, pad8;
+  int16_t             pad16;
+  union p4est_indep_data
+  {
+    void               *unused;
+    p4est_topidx_t      which_tree;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      int                 owner_rank;
+    }
+    piggy1;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_topidx_t      from_tree;
+    }
+    piggy_unused2;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      local_num;
+    }
+    piggy3;
+  }
+  p;
+}
+p8est_indep_t;
+
+/** Store a hanging node that depends on two independent nodes.
+ * Keep this in sync with the p8est_t data structure.
+ */
+typedef struct p8est_hang2
+{
+  p4est_qcoord_t      x, y, z;
+  int8_t              level, pad8;
+  int16_t             pad16;
+  union p8est_hang2_data
+  {
+    void               *unused;
+    p4est_topidx_t      which_tree;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      int                 owner_rank;
+    }
+    piggy_unused1;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_topidx_t      from_tree;
+    }
+    piggy_unused2;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      local_num;
+    }
+    piggy_unused3;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      depends[2];
+    }
+    piggy;
+  }
+  p;
+}
+p8est_hang2_t;
+
+/** Store a hanging node that depends on four independent nodes.
+ * Keep this in sync with the p8est_t data structure.
+ */
+typedef struct p8est_hang4
+{
+  p4est_qcoord_t      x, y, z;
+  int8_t              level, pad8;
+  int16_t             pad16;
+  union p8est_hang4_data
+  {
+    void               *unused;
+    p4est_topidx_t      which_tree;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      int                 owner_rank;
+    }
+    piggy_unused1;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_topidx_t      from_tree;
+    }
+    piggy_unused2;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      local_num;
+    }
+    piggy_unused3;
+    struct
+    {
+      p4est_topidx_t      which_tree;
+      p4est_locidx_t      depends[4];
+    }
+    piggy;
+  }
+  p;
+}
+p8est_hang4_t;
+
+/** This structure holds complete parallel node information.
+ *
+ * Nodes are unique and either independent, face or edge hanging.
+ * Independent nodes store their owner's tree id in piggy3.which_tree.
+ * The index in their owner's ordering is stored in piggy3.local_num.
+ * Hanging nodes store their owner's tree id in piggy.which_tree.
+ * The numbers of their associated independent nodes are in piggy.depends[].
+ *
+ * The local_nodes table is of dimension 8 * num_local_quadrants
+ * and encodes the node indexes for all corners of all quadrants.  Let
+ * ni := indep_nodes.elem_count,
+ * fi := face_hangings.elem_count,
+ * ei := edge_hangings.elem_count.
+ * If for l := local_nodes[k]
+ * l >= 0 && l < ni: l indexes into indep_nodes.
+ * l >= ni && l < ni + fi: l - ni indexes into face_hangings.
+ * l >= ni + fi && l < ni + fi + ei: l - ni - fi indexes into edge_hangings.
+ * No other values for l are permitted.
+ *
+ * The array shared_indeps holds lists of node sharers (not including rank).
+ * The entry shared_indeps[i] is of type sc_recycle_array_t
+ * and holds the list of nodes with i + 1 sharers.
+ * For each independent node, its member pad8 holds the number of sharers
+ * and its member pad16 holds the position in the assigned recycle array
+ * if this number fits into an int16_t.  If this limit is exceeded, the
+ * array shared_offsets is filled with these positions as one p4est_locidx_t
+ * per independent node, and all pad16 members are set to -1.  To recognize
+ * the latter situation you can check for shared_offsets != NULL.
+ *
+ * Each processor owns num_owned_indeps of the stored independent nodes.
+ * The first independent owned node is at index offset_owned_indeps.
+ * The table nonlocal_ranks contains the ranks of all stored non-owned nodes.
+ * The table global_owned_indeps holds the number of owned nodes for each rank.
+ */
+typedef struct p8est_nodes
+{
+  p4est_locidx_t      num_local_quadrants;
+  p4est_locidx_t      num_owned_indeps, num_owned_shared;
+  p4est_locidx_t      offset_owned_indeps;
+  sc_array_t          indep_nodes;
+  sc_array_t          face_hangings;
+  sc_array_t          edge_hangings;
+  p4est_locidx_t     *local_nodes;
+  sc_array_t          shared_indeps;
+  p4est_locidx_t     *shared_offsets;
+  int                *nonlocal_ranks;
+  p4est_locidx_t     *global_owned_indeps;
+}
+p8est_nodes_t;
+
+/** Create node information.
+ * \param [in] ghost    Ghost layer.  If this is NULL, then only
+ *                      tree- and processor-local nodes will be matched
+ *                      and all others duplicated, all nodes will be
+ *                      counted as independent with no sharers, and
+ *                      nodes->global_owned_indeps will be NULL;
+ *                      this also works for a corner-unbalanced forest,
+ *                      but nodes may not be numbered uniquely in this case.
+ */
+p8est_nodes_t      *p8est_nodes_new (p8est_t * p8est, p8est_ghost_t * ghost);
+
+/** Destroy node information. */
+void                p8est_nodes_destroy (p8est_nodes_t * nodes);
+
+/** Check node information for internal consistency. */
+int                 p8est_nodes_is_valid (p8est_t * p8est,
+                                          p8est_nodes_t * nodes);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_NODES_H */
diff --git a/src/p8est_plex.c b/src/p8est_plex.c
new file mode 100644
index 0000000..5a84e28
--- /dev/null
+++ b/src/p8est_plex.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_plex.c"
diff --git a/src/p8est_plex.h b/src/p8est_plex.h
new file mode 100644
index 0000000..dbd528d
--- /dev/null
+++ b/src/p8est_plex.h
@@ -0,0 +1,83 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P8EST_PLEX_H
+#define P8EST_PLEX_H
+
+#include <p8est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Create the data necessary to create a PETsc DMPLEX representation of a
+ * forest.  The forest must be at least edge balanced (see p8est_balance()).
+ * See test/test_plex2.c for example usage.
+ *
+ * All arrays should be initialized to hold sizeof (p4est_locidx_t), except
+ * for \a out_remotes, which should be initialized to hold
+ * (2 * sizeof (p4est_locidx_t)).
+ *
+ * \param[in]     p8est                 the forest
+ * \param[in]     ctype                 the type of adjacency for the overlap
+ * \param[in]     overlap               the number of layers of overlap (zero
+ *                                      is acceptable)
+ * \param[out]    first_local_quad      the local quadrants are assigned
+ *                                      contiguous plex indices, starting with
+ *                                      this index
+ * \param[in,out] out_points_per_dim    filled with argument for
+ *                                      DMPlexCreateFromDAG()
+ * \param[in,out] out_cone_sizes        filled with argument for
+ *                                      DMPlexCreateFromDAG()
+ * \param[in,out] out_cones             filled with argument for
+ *                                      DMPlexCreateFromDAG()
+ * \param[in,out] out_cone_orientations filled with argument for
+ *                                      DMPlexCreateFromDAG()
+ * \param[in,out] out_vertex_coords     filled with argument for
+ *                                      DMPlexCreateFromDAG()
+ * \param[in,out] out_children          filled with argument for
+ *                                      DMPlexSetTree()
+ * \param[in,out] out_parents           filled with argument for
+ *                                      DMPlexSetTree()
+ * \param[in,out] out_childids          filled with argument for
+ *                                      DMPlexSetTree()
+ * \param[in,out] out_leaves            filled with argument for
+ *                                      PetscSFSetGraph()
+ * \param[in,out] out_remotes           filled with argument for
+ *                                      PetscSFSetGraph()
+ */
+void                p8est_get_plex_data (p8est_t * p8est,
+                                         p8est_connect_type_t ctype,
+                                         int overlap,
+                                         p4est_locidx_t * first_local_quad,
+                                         sc_array_t * out_points_per_dim,
+                                         sc_array_t * out_cone_sizes,
+                                         sc_array_t * out_cones,
+                                         sc_array_t * out_cone_orientations,
+                                         sc_array_t * out_vertex_coords,
+                                         sc_array_t * out_children,
+                                         sc_array_t * out_parents,
+                                         sc_array_t * out_childids,
+                                         sc_array_t * out_leaves,
+                                         sc_array_t * out_remotes);
+
+SC_EXTERN_C_END;
+#endif /* P4EST_PLEX_H */
diff --git a/src/p8est_points.c b/src/p8est_points.c
new file mode 100644
index 0000000..b1dc59a
--- /dev/null
+++ b/src/p8est_points.c
@@ -0,0 +1,33 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/********************************************************************
+ *                          IMPORTANT NOTE                          *
+ *                                                                  *
+ * The p4est_points functionality depends on sc/src/sc_sort.        *
+ * That parallel bitonic sort is still buggy (see sc/bugs).         *
+ * If you want to use this code you have to fix the sort first.     *
+ ********************************************************************/
+
+#include <p4est_to_p8est.h>
+#include "p4est_points.c"
diff --git a/src/p8est_points.h b/src/p8est_points.h
new file mode 100644
index 0000000..29b2c03
--- /dev/null
+++ b/src/p8est_points.h
@@ -0,0 +1,74 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/********************************************************************
+ *                          IMPORTANT NOTE                          *
+ *                                                                  *
+ * The p4est_points functionality depends on sc/src/sc_sort.        *
+ * That parallel bitonic sort is still buggy (see sc/bugs).         *
+ * If you want to use this code you have to fix the sort first.     *
+ ********************************************************************/
+
+#ifndef P8EST_POINTS_H
+#define P8EST_POINTS_H
+
+#include <p8est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Create a new forest based on a distributed set of points.
+ *
+ * \param [in] mpicomm       A valid MPI communicator.
+ * \param [in] connectivity  This is the connectivity information that
+ *                           the forest is built with.  Note the p8est
+ *                           does not take ownership of the memory.
+ * \param [in] maxlevel      Level of the smallest possible quadrants.
+ * \param [in] points        Unsorted collection of clamped quadrant nodes.
+ *                           The tree id must be stored in p.which_tree.
+ * \param [in] num_points    Number of local points provided in the array.
+ * \param [in] max_points    Maximum number of points per quadrant.
+ *                           Applies to quadrants above maxlevel, so 0 is ok.
+ *                           A value of -1 disables all refinement.
+ * \param [in] data_size     This is the size of data for each quadrant which
+ *                           can be zero.  Then user_data_pool is set to NULL.
+ * \param [in] init_fn       Callback function to initialize the user_data
+ *                           which is already allocated automatically.
+ * \param [in] user_pointer  Assign to the user_pointer member of the p8est
+ *                           before init_fn is called the first time.
+ *
+ * \return This returns a valid forest.
+ *
+ * \note The connectivity structure must not be destroyed
+ *       during the lifetime of this forest.
+ */
+p8est_t            *p8est_new_points (sc_MPI_Comm mpicomm,
+                                      p8est_connectivity_t * connectivity,
+                                      int maxlevel, p8est_quadrant_t * points,
+                                      p4est_locidx_t num_points,
+                                      p4est_locidx_t max_points,
+                                      size_t data_size, p8est_init_t init_fn,
+                                      void *user_pointer);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_POINTS_H */
diff --git a/src/p8est_search.c b/src/p8est_search.c
new file mode 100644
index 0000000..527cbbb
--- /dev/null
+++ b/src/p8est_search.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_search.c"
diff --git a/src/p8est_search.h b/src/p8est_search.h
new file mode 100644
index 0000000..a726e8e
--- /dev/null
+++ b/src/p8est_search.h
@@ -0,0 +1,159 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P8EST_SEARCH_H
+#define P8EST_SEARCH_H
+
+#include <p8est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** Find the lowest position tq in a quadrant array such that tq >= q.
+ * \return  Returns the id of the matching quadrant
+ *                  or -1 if not found or the array is empty.
+ */
+ssize_t             p8est_find_lower_bound (sc_array_t * array,
+                                            const p8est_quadrant_t * q,
+                                            size_t guess);
+
+/** Find the highest position tq in a quadrant array such that tq <= q.
+ * \return  Returns the id of the matching quadrant
+ *                  or -1 if not found or the array is empty.
+ */
+ssize_t             p8est_find_higher_bound (sc_array_t * array,
+                                             const p8est_quadrant_t * q,
+                                             size_t guess);
+
+/** Given a sorted \a array of quadrants that have a common ancestor at level
+ * \a level, compute the \a indices of the first quadrant in each of the common
+ * ancestor's children at level \a level + 1;
+ * \param [in] array     The sorted array of quadrants of level > \a level.
+ * \param [in] level     The level at which there is a common ancestor.
+ * \param [in,out] indices     The indices of the first quadrant in each of
+ *                             the ancestors's children, plus an additional
+ *                             index on the end.  The quadrants of \a array
+ *                             that are descendants of child i have indices
+ *                             between indices[i] and indices[i + 1] - 1.  If
+ *                             indices[i] = indices[i+1], this indicates that
+ *                             no quadrant in the array is contained in
+ *                             child i.
+ */
+void                p8est_split_array (sc_array_t * array, int level,
+                                       size_t indices[]);
+
+/** Given two smallest quadrants, \a lq and \a uq, that mark the first and the
+ * last quadrant in a range of quadrants, determine which portions of the tree
+ * boundary the range touches.
+ * \param [in] lq        The smallest quadrant at the start of the range: if
+ *                       NULL, the tree's first quadrant is taken to be the
+ *                       start of the range.
+ * \param [in] uq        The smallest quadrant at the end of the range: if
+ *                       NULL, the tree's last quadrant is taken to be the
+ *                       end of the range.
+ * \param [in] level     The level of the containing quadrant whose boundaries
+ *                       are tested: 0 if we want to test the boundaries of the
+ *                       whole tree.
+ * \param [in/out] faces       An array of size 6 that is filled: faces[i] is
+ *                             true if the range touches that face.
+ * \param [in/out] edges       An array of size 12 that is filled: edges[i] is
+ *                             true if the range touches that edge.
+ * \param [in/out] corners     An array of size 8 that is filled: corners[i] is
+ *                             true if the range touches that corner.
+ *                             \faces, \edges or \corners may be NULL.
+ * \return  Returns an int32_t encoded with the same information in \faces,
+ *          \edges and \corners: the first (least) six bits represent the six
+ *          faces, the next twelve bits represent the twelve edges, the next
+ *          eight bits represent the eight corners.
+ */
+int32_t             p8est_find_range_boundaries (p8est_quadrant_t * lq,
+                                                 p8est_quadrant_t * uq,
+                                                 int level, int faces[],
+                                                 int edges[], int corners[]);
+
+/** Callback function to query the match of a "point" with a quadrant.
+ *
+ * This function can be called in two roles:  Per-quadrant, in which case the
+ * parameter \a point is NULL, or per-point, possibly many times per quadrant.
+ *
+ * \param [in] p8est        The forest to be queried.
+ * \param [in] which_tree   The tree id under consideration.
+ * \param [in] quadrant     The quadrant under consideration.
+ *                          This quadrant may be coarser than the quadrants
+ *                          that are contained in the forest (an ancestor), in
+ *                          which case it is a temporary variable and not part
+ *                          of the forest storage.  Otherwise, it is a leaf and
+ *                          points directly into the forest storage.
+ * \param [in] local_num    If the quadrant is not a leaf, this is -1.  Otherwise
+ *                          it is the (non-negative) index of the quadrant
+ *                          relative to the processor-local quadrant storage.
+ * \param [in] point        Representation of a "point"; user-defined.
+ *                          If \a point is NULL, the callback may be used to
+ *                          prepare quadrant-related search meta data.
+ * \return                  If \a point is NULL, true if the search confined to
+ *                          \a quadrant should be executed, false to skip it.
+ *                          Else, true if point may be contained in the
+ *                          quadrant and false otherwise; the return value has
+ *                          no effect on a leaf.
+ */
+typedef int         (*p8est_search_query_t) (p8est_t * p8est,
+                                             p4est_topidx_t which_tree,
+                                             p8est_quadrant_t * quadrant,
+                                             p4est_locidx_t local_num,
+                                             void *point);
+
+/** Search "points" from a given set in the forest.
+ *
+ * The search runs over all local quadrants and proceeds recursively top-down.
+ * Its outer loop is thus a depth-first, processor-local forest traversal.
+ * Each quadrant in that loop either is a leaf, or a (direct or indirect)
+ * strict ancestor of a leaf.  On entering a new quadrant, a user-provided
+ * quadrant-callback is executed.
+ * The set of points that potentially matches a given quadrant diminishes from
+ * the root down to the leaves:  For each quadrant, an inner loop over the
+ * potentially matching points executes a point-callback for each candidate
+ * that determines whether the point may be a match.  If not, it is discarded
+ * immediately, otherwise it is passed to the next finer level.
+ * The callback is allowed to return true for the same point and more than one
+ * quadrant; in this case more than one matching quadrant may be identified.
+ * The callback is also allowed to return false for all children of a quadrant
+ * that it returned true for earlier.
+ * The points can really be anything, p4est does not perform any
+ * interpretation, just passes the pointer along to the callback function.
+ *
+ * \param [in] p8est        The forest to be searched.
+ * \param [in] search_quadrant_fn   Executed once for each quadrant that is
+ *                          entered.  This quadrant is always local.  If the
+ *                          callback returns false, this quadrant and its
+ *                          descendants are excluded from the search.
+ *                          May be NULL in which case it is ignored.
+ * \param [in] search_point_fn      Must return true for a possible match.
+ * \param [in] points       User-defined array of "points".
+ */
+void                p8est_search (p8est_t * p8est,
+                                  p8est_search_query_t search_quadrant_fn,
+                                  p8est_search_query_t search_point_fn,
+                                  sc_array_t * points);
+
+SC_EXTERN_C_END;
+
+#endif
diff --git a/src/p8est_tets_hexes.c b/src/p8est_tets_hexes.c
new file mode 100644
index 0000000..6fe97d1
--- /dev/null
+++ b/src/p8est_tets_hexes.c
@@ -0,0 +1,719 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p8est_tets_hexes.h>
+
+/* *INDENT-OFF* */
+static const int
+p8est_tet_edge_corners[6][2] =
+{{0, 1},
+ {0, 2},
+ {0, 3},
+ {1, 2},
+ {1, 3},
+ {2, 3}};
+
+static const int
+p8est_tet_face_corners[4][3] =
+{{0, 1, 2},
+ {0, 1, 3},
+ {0, 2, 3},
+ {1, 2, 3}};
+
+static const int
+p8est_tet_tree_nodes[4][8] =
+{{ 0, 4, 5, 10, 6, 11, 12, 14 },
+ { 4, 1, 10, 7, 11, 8, 14, 13 },
+ { 5, 10, 2, 7, 12, 14, 9, 13 },
+ { 6, 11, 12, 14, 3, 8, 9, 13 }};
+/* *INDENT-ON* */
+
+static inline double *
+p8est_tets_node_index (p8est_tets_t * ptg, size_t nodeno)
+{
+  return (double *) sc_array_index (ptg->nodes, 3 * nodeno);
+}
+
+static inline p4est_topidx_t *
+p8est_tets_tet_index (p8est_tets_t * ptg, size_t tetno)
+{
+  return (p4est_topidx_t *) sc_array_index (ptg->tets, 4 * tetno);
+}
+
+sc_array_t         *
+p8est_tets_read_node (const char *nodefilename)
+{
+  int                 retval;
+  int                 k;
+  int                 dims, num_attributes, boundary_marker;
+  long                jl, lnum_nodes;
+  double             *pc;
+  size_t              iz, znum_nodes;
+  FILE               *nodefile;
+  sc_array_t         *nodes;
+
+  /* prepare cleanup on error */
+  nodefile = NULL;
+  nodes = NULL;
+
+  /* open node file */
+  nodefile = fopen (nodefilename, "rb");
+  if (nodefile == NULL) {
+    P4EST_LERRORF ("Failed to open %s\n", nodefilename);
+    goto dead;
+  }
+
+  /* read header information */
+  retval = fscanf (nodefile, "%ld %d %d %d",
+                   &lnum_nodes, &dims, &num_attributes, &boundary_marker) - 4;
+  if (retval || lnum_nodes < 0 || lnum_nodes > P4EST_TOPIDX_MAX
+      || dims != 3 || num_attributes < 0) {
+    P4EST_LERROR ("Failed to read node header\n");
+    goto dead;
+  }
+  znum_nodes = (size_t) lnum_nodes;
+
+  /* read node coordinates */
+  nodes = sc_array_new_size (sizeof (double), 3 * znum_nodes);
+  for (iz = 0; iz < znum_nodes; ++iz) {
+    pc = (double *) sc_array_index (nodes, 3 * iz);
+    retval = fscanf (nodefile, "%ld %lf %lf %lf",
+                     &jl, pc, pc + 1, pc + 2) - 4;
+    if (retval || (long) iz != jl) {
+      P4EST_LERRORF ("Failed to read node %ld coordinates\n", (long) iz);
+      goto dead;
+    }
+    for (k = 0; k < num_attributes; ++k) {
+      retval = fscanf (nodefile, "%*f");
+      if (retval != 0) {
+        P4EST_LERRORF ("Failed to read node %ld attribute %d\n", (long) iz,
+                       k);
+        goto dead;
+      }
+    }
+    if (boundary_marker) {
+      retval = fscanf (nodefile, "%*d");
+      if (retval != 0) {
+        P4EST_LERRORF ("Failed to read node %ld boundary marker\n",
+                       (long) iz);
+        goto dead;
+      }
+    }
+  }
+
+  /* close node file and return */
+  retval = fclose (nodefile);
+  nodefile = NULL;
+  if (retval) {
+    P4EST_LERRORF ("Failed to close %s\n", nodefilename);
+    goto dead;
+  }
+  return nodes;
+
+dead:
+  /* clean up on error */
+  if (nodefile != NULL) {
+    fclose (nodefile);
+  }
+  if (nodes != NULL) {
+    sc_array_destroy (nodes);
+  }
+  return NULL;
+}
+
+sc_array_t         *
+p8est_tets_read_ele (const char *elefilename, p4est_topidx_t num_nodes,
+                     sc_array_t ** attributes)
+{
+  int                 retval;
+  int                 k;
+  int                 nodespertet, region;
+  long                jl, lnum_tets, lmax_nodes;
+  long                nl[4];
+  size_t              iz, znum_tets;
+  int                *pi;
+  p4est_topidx_t     *pt;
+  FILE               *elefile;
+  sc_array_t         *tets, *attr;
+
+  /* prepare cleanup on error */
+  elefile = NULL;
+  tets = NULL;
+  attr = NULL;
+  if (attributes != NULL) {
+    *attributes = NULL;
+  }
+  lmax_nodes = (num_nodes >= 0 ? (long) num_nodes : P4EST_TOPIDX_MAX);
+
+  /* open ele file */
+  elefile = fopen (elefilename, "rb");
+  if (elefile == NULL) {
+    P4EST_LERRORF ("Failed to open %s\n", elefilename);
+    goto dead;
+  }
+
+  /* read header information */
+  retval = fscanf (elefile, "%ld %d %d",
+                   &lnum_tets, &nodespertet, &region) - 3;
+  if (retval || lnum_tets < 0 || lnum_tets > P4EST_TOPIDX_MAX
+      || nodespertet != 4) {
+    P4EST_LERROR ("Failed to read tet header\n");
+    goto dead;
+  }
+  znum_tets = (size_t) lnum_tets;
+
+  /* read tet coordinates */
+  tets = sc_array_new_size (sizeof (p4est_topidx_t), 4 * znum_tets);
+  if (region && attributes != NULL) {
+    attr = *attributes = sc_array_new_size (sizeof (int), znum_tets);
+  }
+  for (iz = 0; iz < znum_tets; ++iz) {
+    pt = (p4est_topidx_t *) sc_array_index (tets, 4 * iz);
+    retval = fscanf (elefile, "%ld %ld %ld %ld %ld",
+                     &jl, nl, nl + 1, nl + 2, nl + 3) - 5;
+    if (retval || (long) iz != jl) {
+      P4EST_LERRORF ("Failed to read tet %ld node numbers\n", (long) iz);
+      goto dead;
+    }
+    for (k = 0; k < 4; ++k) {
+      if (nl[k] < 0 || nl[k] > lmax_nodes) {
+        P4EST_LERRORF ("Tet %ld has invalid node number %d\n", (long) iz, k);
+        goto dead;
+      }
+      pt[k] = (p4est_topidx_t) nl[k];
+    }
+    if (region) {
+      if (attr != NULL) {
+        pi = (int *) sc_array_index (attr, iz);
+        retval = fscanf (elefile, "%d", pi) - 1;
+      }
+      else {
+        retval = fscanf (elefile, "%*d");
+      }
+      if (retval != 0) {
+        P4EST_LERRORF ("Failed to read tet %ld region attribute\n",
+                       (long) iz);
+        goto dead;
+      }
+    }
+  }
+
+  /* close ele file */
+  retval = fclose (elefile);
+  elefile = NULL;
+  if (retval) {
+    P4EST_LERRORF ("Failed to close %s\n", elefilename);
+    goto dead;
+  }
+  return tets;
+
+dead:
+  /* clean up on error */
+  if (elefile != NULL) {
+    fclose (elefile);
+  }
+  if (tets != NULL) {
+    sc_array_destroy (tets);
+  }
+  if (attr != NULL) {
+    sc_array_destroy (attr);
+    *attributes = NULL;
+  }
+  return NULL;
+}
+
+p8est_tets_t       *
+p8est_tets_read (const char *tetgenbasename)
+{
+  p4est_topidx_t      num_nodes;
+  char                nodefilename[BUFSIZ];
+  char                elefilename[BUFSIZ];
+  sc_array_t         *nodes, *tets, *attr;
+  p8est_tets_t       *ptg;
+
+  /* prepare cleanup */
+  nodes = tets = attr = NULL;
+  ptg = P4EST_ALLOC (p8est_tets_t, 1);
+
+  /* read nodes */
+  snprintf (nodefilename, BUFSIZ, "%s.node", tetgenbasename);
+  nodes = ptg->nodes = p8est_tets_read_node (nodefilename);
+  if (nodes == NULL) {
+    P4EST_LERRORF ("Failed to read nodes for %s\n", tetgenbasename);
+    goto dead;
+  }
+  num_nodes = (p4est_topidx_t) (nodes->elem_count / 3);
+
+  /* read tetrahedra */
+  snprintf (elefilename, BUFSIZ, "%s.ele", tetgenbasename);
+  tets = ptg->tets = p8est_tets_read_ele (elefilename, num_nodes, &attr);
+  if (tets == NULL) {
+    P4EST_ASSERT (attr == NULL);
+    P4EST_LERRORF ("Failed to read tetrahedra for %s\n", tetgenbasename);
+    goto dead;
+  }
+  ptg->tet_attributes = attr;
+
+  /* we are successful */
+  return ptg;
+
+dead:
+  /* clean up on error */
+  if (nodes != NULL) {
+    sc_array_destroy (nodes);
+  }
+  if (tets != NULL) {
+    sc_array_destroy (tets);
+  }
+  if (attr != NULL) {
+    sc_array_destroy (attr);
+  }
+  P4EST_FREE (ptg);
+  return NULL;
+}
+
+void
+p8est_tets_destroy (p8est_tets_t * ptg)
+{
+  sc_array_destroy (ptg->nodes);
+  sc_array_destroy (ptg->tets);
+  if (ptg->tet_attributes != NULL) {
+    sc_array_destroy (ptg->tet_attributes);
+  }
+
+  P4EST_FREE (ptg);
+}
+
+static int
+p8est_tet_is_righthanded (sc_array_t * nodes, p4est_topidx_t * tet)
+{
+  int                 i, j;
+  double             *nc[4];
+  double              v0[3], v1[3], v2[3], cross01[3];
+  double              vol;
+
+  /* compute tet volume */
+  for (i = 0; i < 4; ++i) {
+    nc[i] = (double *) sc_array_index (nodes, (size_t) (3 * tet[i]));
+  }
+  for (j = 0; j < 3; ++j) {
+    v0[j] = nc[1][j] - nc[0][j];
+    v1[j] = nc[2][j] - nc[0][j];
+    v2[j] = nc[3][j] - nc[0][j];
+  }
+  cross01[0] = v0[1] * v1[2] - v0[2] * v1[1];
+  cross01[1] = v0[2] * v1[0] - v0[0] * v1[2];
+  cross01[2] = v0[0] * v1[1] - v0[1] * v1[0];
+  vol = 0.;
+  for (j = 0; j < 3; ++j) {
+    vol += cross01[j] * v2[j];
+  }
+  vol *= 1. / 3.;
+
+  return vol >= 0.;
+}
+
+static void
+p8est_tet_flip (p4est_topidx_t * tet)
+{
+  p4est_topidx_t      temp;
+
+  temp = tet[3];
+  tet[3] = tet[2];
+  tet[2] = temp;
+}
+
+p4est_topidx_t
+p8est_tets_make_righthanded (p8est_tets_t * ptg)
+{
+  size_t              iz, znum_tets;
+  p4est_topidx_t      tnum_flips;
+  p4est_topidx_t     *tet;
+
+  tnum_flips = 0;
+  znum_tets = ptg->tets->elem_count / 4;
+  for (iz = 0; iz < znum_tets; ++iz) {
+    tet = p8est_tets_tet_index (ptg, iz);
+    if (!p8est_tet_is_righthanded (ptg->nodes, tet)) {
+      p8est_tet_flip (tet);
+      P4EST_ASSERT (p8est_tet_is_righthanded (ptg->nodes, tet));
+      ++tnum_flips;
+    }
+  }
+
+  return tnum_flips;
+}
+
+typedef struct p8est_tet_edge_info
+{
+  p4est_topidx_t      ek[2];
+  sc_array_t          tets;
+  sc_array_t          tet_edges;
+}
+p8est_tet_edge_info_t;
+
+/** Create unique edge key for a given edge of a tetrahedron.
+ * \param [out] ek      The edge key consists of two node numbers.
+ * \param [in] tet      A tetrahedron referring to node indices.
+ * \param [in] edge     Tetrahedron edge number in [ 0 .. 5 ].
+ */
+static void
+p8est_tet_edge_key (p4est_topidx_t * ek, p4est_topidx_t * tet, int edge)
+{
+  P4EST_ASSERT (0 <= edge && edge < 6);
+
+  ek[0] = tet[p8est_tet_edge_corners[edge][0]];
+  ek[1] = tet[p8est_tet_edge_corners[edge][1]];
+
+  P4EST_ASSERT (ek[0] != ek[1]);
+  p4est_topidx_bsort (ek, 2);
+}
+
+static unsigned
+p8est_tet_edge_hash (const void *v, const void *u)
+{
+  const p8est_tet_edge_info_t *ei = (p8est_tet_edge_info_t *) v;
+
+  return p4est_topidx_hash2 (ei->ek);
+}
+
+static int
+p8est_tet_edge_equal (const void *v1, const void *v2, const void *u)
+{
+  const p8est_tet_edge_info_t *ei1 = (p8est_tet_edge_info_t *) v1;
+  const p8est_tet_edge_info_t *ei2 = (p8est_tet_edge_info_t *) v2;
+
+  return !memcmp (ei1->ek, ei2->ek, 2 * sizeof (p4est_topidx_t));
+}
+
+static sc_hash_array_t *
+p8est_tets_identify_edges (p8est_tets_t * ptg)
+{
+  int                 edge, *pi;
+  size_t              iz, znum_tets, pz;
+  sc_hash_array_t    *edge_ha;
+  p4est_topidx_t     *tet, *pt;
+  p8est_tet_edge_info_t eikey, *ei;
+
+  /* create hash array for shared edges */
+  edge_ha = sc_hash_array_new (sizeof (p8est_tet_edge_info_t),
+                               p8est_tet_edge_hash, p8est_tet_edge_equal,
+                               NULL);
+
+  /* loop through all edges and identify edge-neighbor tet groups */
+  znum_tets = ptg->tets->elem_count / 4;
+  for (iz = 0; iz < znum_tets; ++iz) {
+    tet = p8est_tets_tet_index (ptg, iz);
+    for (edge = 0; edge < 6; ++edge) {
+      p8est_tet_edge_key (eikey.ek, tet, edge);
+      ei = (p8est_tet_edge_info_t *)
+        sc_hash_array_insert_unique (edge_ha, &eikey, &pz);
+      if (ei != NULL) {
+        /* added new edge group ei to hash array */
+        P4EST_ASSERT (sc_array_position (&edge_ha->a, ei) == pz);
+        memcpy (ei->ek, eikey.ek, 2 * sizeof (p4est_topidx_t));
+        sc_array_init (&ei->tets, sizeof (p4est_topidx_t));
+        pt = (p4est_topidx_t *) sc_array_push (&ei->tets);
+        *pt = (p4est_topidx_t) iz;
+        sc_array_init (&ei->tet_edges, sizeof (int));
+        pi = (int *) sc_array_push (&ei->tet_edges);
+        *pi = edge;
+      }
+      else {
+        /* found existing entry from earlier edge */
+        ei = (p8est_tet_edge_info_t *) sc_array_index (&edge_ha->a, pz);
+        P4EST_ASSERT (p8est_tet_edge_equal (ei->ek, eikey.ek, NULL));
+        P4EST_ASSERT (ei->tets.elem_count > 0);
+        pt = (p4est_topidx_t *) sc_array_push (&ei->tets);
+        *pt = (p4est_topidx_t) iz;
+        P4EST_ASSERT (ei->tet_edges.elem_count > 0);
+        pi = (int *) sc_array_push (&ei->tet_edges);
+        *pi = edge;
+      }
+    }
+  }
+
+  return edge_ha;
+}
+
+typedef struct p8est_tet_face_info
+{
+  p4est_topidx_t      fk[3];
+  p4est_topidx_t      tets[2];
+  int                 tet_faces[2];
+}
+p8est_tet_face_info_t;
+
+/** Create unique face key for a given face of a tetrahedron.
+ * \param [out] fk      The face key consists of three node numbers.
+ * \param [in] tet      A tetrahedron referring to node indices.
+ * \param [in] face     Tetrahedron face number in [ 0 .. 3 ].
+ */
+static void
+p8est_tet_face_key (p4est_topidx_t * fk, p4est_topidx_t * tet, int face)
+{
+  P4EST_ASSERT (0 <= face && face < 4);
+
+  fk[0] = tet[p8est_tet_face_corners[face][0]];
+  fk[1] = tet[p8est_tet_face_corners[face][1]];
+  fk[2] = tet[p8est_tet_face_corners[face][2]];
+
+  P4EST_ASSERT (fk[0] != fk[1] && fk[0] != fk[2] && fk[1] != fk[2]);
+  p4est_topidx_bsort (fk, 3);
+}
+
+static unsigned
+p8est_tet_face_hash (const void *v, const void *u)
+{
+  const p8est_tet_face_info_t *fi = (p8est_tet_face_info_t *) v;
+
+  return p4est_topidx_hash3 (fi->fk);
+}
+
+static int
+p8est_tet_face_equal (const void *v1, const void *v2, const void *u)
+{
+  const p8est_tet_face_info_t *fi1 = (p8est_tet_face_info_t *) v1;
+  const p8est_tet_face_info_t *fi2 = (p8est_tet_face_info_t *) v2;
+
+  return !memcmp (fi1->fk, fi2->fk, 3 * sizeof (p4est_topidx_t));
+}
+
+static sc_hash_array_t *
+p8est_tets_identify_faces (p8est_tets_t * ptg)
+{
+  int                 face;
+  size_t              iz, znum_tets, pz;
+  sc_hash_array_t    *face_ha;
+  p4est_topidx_t     *tet;
+  p8est_tet_face_info_t fikey, *fi;
+
+  /* create hash array for shared faces */
+  face_ha = sc_hash_array_new (sizeof (p8est_tet_face_info_t),
+                               p8est_tet_face_hash, p8est_tet_face_equal,
+                               NULL);
+
+  /* loop through all faces and identify face-neighbor tet pairs */
+  znum_tets = ptg->tets->elem_count / 4;
+  for (iz = 0; iz < znum_tets; ++iz) {
+    tet = p8est_tets_tet_index (ptg, iz);
+    for (face = 0; face < 4; ++face) {
+      p8est_tet_face_key (fikey.fk, tet, face);
+      fi = (p8est_tet_face_info_t *)
+        sc_hash_array_insert_unique (face_ha, &fikey, &pz);
+      if (fi != NULL) {
+        /* added fi to hash array as the first of two tets */
+        P4EST_ASSERT (sc_array_position (&face_ha->a, fi) == pz);
+        memcpy (fi->fk, fikey.fk, 3 * sizeof (p4est_topidx_t));
+        fi->tets[0] = (p4est_topidx_t) iz;
+        fi->tets[1] = -1;
+        fi->tet_faces[0] = face;
+        fi->tet_faces[1] = -1;
+      }
+      else {
+        /* found existing entry from the first face */
+        fi = (p8est_tet_face_info_t *) sc_array_index (&face_ha->a, pz);
+        P4EST_ASSERT (p8est_tet_face_equal (fi->fk, fikey.fk, NULL));
+        P4EST_ASSERT (fi->tets[0] >= 0 && fi->tet_faces[0] >= 0);
+        P4EST_ASSERT (fi->tets[1] == -1 && fi->tet_faces[1] == -1);
+        fi->tets[1] = (p4est_topidx_t) iz;
+        fi->tet_faces[1] = face;
+      }
+    }
+  }
+
+  return face_ha;
+}
+
+/** Create a connectivity where the trees are not connected to each other. */
+static p8est_connectivity_t *
+p8est_tets_connectivity_new (p8est_tets_t * ptg,
+                             sc_hash_array_t * edge_ha,
+                             sc_hash_array_t * face_ha)
+{
+  int                 j, k;
+  int                 edge, face;
+  size_t              nvz, evzoffset, fvzoffset, vvzoffset;
+  size_t              iz, pz;
+  double             *vp, *n[4];
+  int8_t             *ttf;
+  p4est_topidx_t      tt, *tet, node;
+  p4est_topidx_t     *ttv, *ttt;
+  p4est_topidx_t      nid[15];
+  p8est_connectivity_t *conn;
+  p8est_tet_edge_info_t *ei, eikey;
+  p8est_tet_face_info_t *fi, fikey;
+
+  /* arrange vertices by tet corners, edges, faces, and volumes */
+  evzoffset = ptg->nodes->elem_count / 3;
+  fvzoffset = evzoffset + edge_ha->a.elem_count;
+  vvzoffset = fvzoffset + face_ha->a.elem_count;
+  nvz = vvzoffset + ptg->tets->elem_count / 4;
+
+  /* allocate connectivity */
+  conn = p8est_connectivity_new (nvz, ptg->tets->elem_count, 0, 0, 0, 0);
+
+  /* populate vertices */
+  memcpy (conn->vertices, ptg->nodes->array, 3 * evzoffset * sizeof (double));
+  vp = conn->vertices + 3 * evzoffset;
+  for (iz = 0; iz < edge_ha->a.elem_count; ++iz) {
+    ei = (p8est_tet_edge_info_t *) sc_array_index (&edge_ha->a, iz);
+    tt = *((p4est_topidx_t *) sc_array_index (&ei->tets, 0));
+    edge = *((int *) sc_array_index (&ei->tet_edges, 0));
+    tet = p8est_tets_tet_index (ptg, tt);
+    for (j = 0; j < 2; ++j) {
+      node = tet[p8est_tet_edge_corners[edge][j]];
+      n[j] = p8est_tets_node_index (ptg, node);
+    }
+    vp[0] = .5 * (n[0][0] + n[1][0]);
+    vp[1] = .5 * (n[0][1] + n[1][1]);
+    vp[2] = .5 * (n[0][2] + n[1][2]);
+    vp += 3;
+  }
+  for (iz = 0; iz < face_ha->a.elem_count; ++iz) {
+    fi = (p8est_tet_face_info_t *) sc_array_index (&face_ha->a, iz);
+    tt = fi->tets[0];
+    face = fi->tet_faces[0];
+    tet = p8est_tets_tet_index (ptg, tt);
+    for (j = 0; j < 3; ++j) {
+      node = tet[p8est_tet_face_corners[face][j]];
+      n[j] = p8est_tets_node_index (ptg, node);
+    }
+    vp[0] = (1. / 3.) * (n[0][0] + n[1][0] + n[2][0]);
+    vp[1] = (1. / 3.) * (n[0][1] + n[1][1] + n[2][1]);
+    vp[2] = (1. / 3.) * (n[0][2] + n[1][2] + n[2][2]);
+    vp += 3;
+  }
+  for (iz = 0; iz < ptg->tets->elem_count / 4; ++iz) {
+    tet = p8est_tets_tet_index (ptg, iz);
+    for (j = 0; j < 4; ++j) {
+      n[j] = p8est_tets_node_index (ptg, tet[j]);
+    }
+    vp[0] = .25 * (n[0][0] + n[1][0] + n[2][0] + n[3][0]);
+    vp[1] = .25 * (n[0][1] + n[1][1] + n[2][1] + n[3][1]);
+    vp[2] = .25 * (n[0][2] + n[1][2] + n[2][2] + n[3][2]);
+    vp += 3;
+  }
+
+  /* associate forest trees with vertices */
+  ttv = conn->tree_to_vertex;
+  for (iz = 0; iz < ptg->tets->elem_count / 4; ++iz) {
+    tet = p8est_tets_tet_index (ptg, iz);
+
+    /* look up node numbers for all vertices in this tetrahedron */
+    for (j = 0; j < 4; ++j) {
+      nid[j] = tet[j];
+    }
+    for (edge = 0; edge < 6; ++edge) {
+      p8est_tet_edge_key (eikey.ek, tet, edge);
+      P4EST_EXECUTE_ASSERT_TRUE (sc_hash_array_lookup (edge_ha, &eikey, &pz));
+      nid[4 + edge] = (p4est_topidx_t) (evzoffset + pz);
+    }
+    for (face = 0; face < 4; ++face) {
+      p8est_tet_face_key (fikey.fk, tet, face);
+      P4EST_EXECUTE_ASSERT_TRUE (sc_hash_array_lookup (face_ha, &fikey, &pz));
+      nid[10 + face] = (p4est_topidx_t) (fvzoffset + pz);
+    }
+    nid[14] = (p4est_topidx_t) (vvzoffset + iz);
+
+    /* create four trees from this tetrahedron */
+    for (j = 0; j < 4; ++j) {
+      for (k = 0; k < P8EST_CHILDREN; ++k) {
+        *ttv++ = nid[p8est_tet_tree_nodes[j][k]];
+      }
+    }
+  }
+
+  /* create neighborhood information for isolated trees */
+  ttt = conn->tree_to_tree;
+  ttf = conn->tree_to_face;
+  for (tt = 0; tt < conn->num_trees; ++tt) {
+    for (face = 0; face < P8EST_FACES; ++face) {
+      ttt[face] = tt;
+      ttf[face] = (int8_t) face;
+    }
+    ttt += P8EST_FACES;
+    ttf += P8EST_FACES;
+  }
+
+  return conn;
+}
+
+p8est_connectivity_t *
+p8est_connectivity_new_tets (p8est_tets_t * ptg)
+{
+  int                *pint, i;
+  int8_t              attr;
+  size_t              ez, znum_edges;
+  size_t              tz, znum_tets;
+  sc_hash_array_t    *edge_ha, *face_ha;
+  sc_array_t          edge_array;
+  p8est_tet_edge_info_t *ei;
+  p8est_connectivity_t *conn;
+
+  /* identify unique edges and faces */
+  edge_ha = p8est_tets_identify_edges (ptg);
+  P4EST_GLOBAL_LDEBUGF ("Added %ld unique tetrahedron edges\n",
+                        (long) edge_ha->a.elem_count);
+
+  face_ha = p8est_tets_identify_faces (ptg);
+  P4EST_GLOBAL_LDEBUGF ("Added %ld unique tetrahedron faces\n",
+                        (long) face_ha->a.elem_count);
+
+  /* add vertex information to connectivity */
+  conn = p8est_tets_connectivity_new (ptg, edge_ha, face_ha);
+  P4EST_GLOBAL_LDEBUGF ("Connectivity has %ld vertices and %ld trees\n",
+                        (long) conn->num_vertices, (long) conn->num_trees);
+
+  /* clean unique edges and faces */
+  sc_hash_array_rip (edge_ha, &edge_array);
+  znum_edges = edge_array.elem_count;
+  for (ez = 0; ez < znum_edges; ++ez) {
+    ei = (p8est_tet_edge_info_t *) sc_array_index (&edge_array, ez);
+    sc_array_reset (&ei->tets);
+    sc_array_reset (&ei->tet_edges);
+  }
+  sc_array_reset (&edge_array);
+  sc_hash_array_destroy (face_ha);
+
+  /* transfer tree tags */
+  if (ptg->tet_attributes != NULL) {
+    znum_tets = ptg->tet_attributes->elem_count;
+    P4EST_ASSERT (4 * znum_tets == (size_t) conn->num_trees);
+    p8est_connectivity_set_attr (conn, 1);
+    for (tz = 0; tz < znum_tets; ++tz) {
+      pint = (int *) sc_array_index (ptg->tet_attributes, tz);
+      attr = (int8_t) pint[0];
+      for (i = 0; i < 4; ++i) {
+        conn->tree_to_attr[4 * tz + i] = attr;
+      }
+    }
+  }
+
+  /* connect p4est tree through faces, edges, and corners */
+  p8est_connectivity_complete (conn);
+  P4EST_GLOBAL_LDEBUGF ("Connectivity has %ld edges and %ld corners\n",
+                        (long) conn->num_edges, (long) conn->num_corners);
+
+  return conn;
+}
diff --git a/src/p8est_tets_hexes.h b/src/p8est_tets_hexes.h
new file mode 100644
index 0000000..a552132
--- /dev/null
+++ b/src/p8est_tets_hexes.h
@@ -0,0 +1,87 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P8EST_TETS_HEXES_H
+#define P8EST_TETS_HEXES_H
+
+#include <p8est_connectivity.h>
+
+typedef struct p8est_tets
+{
+  /** The node array contains a triplet of double coordinates per node. */
+  sc_array_t         *nodes;
+
+  /** The tet array contains a quartet of p4est_topidx_t nodes per tet. */
+  sc_array_t         *tets;
+
+  /** The element_attributes array can contain one int attribute per tet. */
+  sc_array_t         *tet_attributes;
+}
+p8est_tets_t;
+
+/** Read nodes from a tetgen .node file.
+ * \param [in] nodefile     Name of file in tetgen .node format.
+ * \return                  An array with three double coordinates per node,
+ *                          or NULL on file error.
+ */
+sc_array_t         *p8est_tets_read_node (const char *nodefile);
+
+/** Read tetrahedra from a tetgen .ele file.
+ * \param [in] elefile          Name of file in tetgen .ele format.
+ * \param [in] num_nodes        If nonnegative, (exclusive) upper node number.
+ * \param [in,out] attributes   If not NULL, an array will be created
+ *                              if the .ele file contains attributes.
+ * \return                      An array with four p4est_topidx_t nodes
+ *                              per tet, or NULL on file error.
+ */
+sc_array_t         *p8est_tets_read_ele (const char *elefile,
+                                         p4est_topidx_t num_nodes,
+                                         sc_array_t ** attributes);
+
+/** Read element and node information from a tetgen base name.
+ * The names for element and node files are derived from base name by suffix.
+ * \param [in] tetgenbasename   Base name for tetgen files (without suffix).
+ * \return                      A populated p8est_tets_t structure
+ *                              or NULL on file error.
+ */
+p8est_tets_t       *p8est_tets_read (const char *tetgenbasename);
+
+/** Destroy all memory associated with a p8est_tets_t structure.
+ * \param [in] ptg          Allocated p8est_tets_t structure.
+ */
+void                p8est_tets_destroy (p8est_tets_t * ptg);
+
+/** Change all left-handed tetrahedra to right-handed ones.
+ * \param [in,out] ptg      Structure with node and tet information.
+ * \return                  The number of tets that were flipped.
+ */
+p4est_topidx_t      p8est_tets_make_righthanded (p8est_tets_t * ptg);
+
+/** Create a fully populated connectivity structure from tetgen information.
+ * \param [in] ptg  A p8est_tets_t structure with node and tet information.
+ *                  If it contains attributes they will be cast with (int8_t).
+ * \return          Connectivity (free with p8est_connectivity_destroy).
+ */
+p8est_connectivity_t *p8est_connectivity_new_tets (p8est_tets_t * ptg);
+
+#endif /* !P8EST_TETS_HEXES */
diff --git a/src/p8est_vtk.c b/src/p8est_vtk.c
new file mode 100644
index 0000000..b4afc09
--- /dev/null
+++ b/src/p8est_vtk.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_vtk.c"
diff --git a/src/p8est_vtk.h b/src/p8est_vtk.h
new file mode 100644
index 0000000..ef38265
--- /dev/null
+++ b/src/p8est_vtk.h
@@ -0,0 +1,195 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/** \file p8est_vtk.h
+ *
+ * Routines for printing a forest and associated fields to vtk format.
+ *
+ * \ingroup p8est
+ */
+
+#ifndef P8EST_VTK_H
+#define P8EST_VTK_H
+
+#include <p8est_geometry.h>
+#include <p8est.h>
+
+SC_EXTERN_C_BEGIN;
+
+/** This writes out the p8est in VTK format.
+ *
+ * This is a convenience function for the special
+ * case of writing out the tree id, quadrant level, and MPI rank only.
+ * One file is written per MPI rank, and one meta file on rank 0.
+ * This function will abort if there is a file error.
+ *
+ * \param [in] p8est    The p8est to be written.
+ * \param [in] geom     A p8est_geometry_t structure or NULL for vertex space.
+ * \param [in] filename The first part of the file name which will have the
+ *                      MPI rank appended to it: The output file will be
+ *                      filename_procNum.vtu, and the meta file filename.pvtu.
+ */
+void                p8est_vtk_write_file (p8est_t * p8est,
+                                          p8est_geometry_t * geom,
+                                          const char *filename);
+
+/** This writes out the p8est and any number of point fields in VTK format.
+ *
+ * This is a convenience function that will abort if there is a file error.
+ *
+ * \param [in] p8est    The p8est to be written.
+ * \param [in] geom     A p8est_geometry_t structure or NULL for vertex space.
+ * \param [in] scale    Double value between 0 and 1 to scale each quadrant.
+ * \param [in] write_tree   Include the tree id as output field.
+ * \param [in] write_level  Include the tree levels as output field.
+ * \param [in] write_rank   Include the MPI rank as output field.
+ * \param [in] wrap_tree    The MPI rank is written module wrap_tree, or 0.
+ * \param filename      First part of the name, see p8est_vtk_write_file.
+ * \param num_scalars   Number of scalar fields to write.
+ * \param num_vectors   Number of vector fields to write.
+ *
+ * The variable arguments need to be pairs of (fieldname, fieldvalues)
+ * where the scalars come first, then the vectors.
+ */
+void                p8est_vtk_write_all (p8est_t * p8est,
+                                         p8est_geometry_t * geom,
+                                         double scale,
+                                         int write_tree, int write_level,
+                                         int write_rank, int wrap_rank,
+                                         int num_scalars, int num_vectors,
+                                         const char *filename, ...);
+
+/** This will write the header of the vtu file.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of
+ * fields.  The calling sequence would be something like
+ *
+ * \begincode
+ * p8est_vtk_write_header(p8est, geom, 1., 1, 1, 1, 0, "output");
+ * p8est_vtk_write_point_scalar (...);
+ * ...
+ * p8est_vtk_write_footer(p8est, "output");
+ * \endcode
+ *
+ * \param p8est     The p8est to be written.
+ * \param geom      A p8est_geometry_t structure or NULL for vertex space.
+ * \param scale     The relative length factor of the quadrants.
+ *                  Use 1.0 to fit quadrants exactly, less to create gaps.
+ * \param write_tree    Boolean to determine if the tree id should be output.
+ * \param write_level   Boolean to determine if the tree levels should be output.
+ * \param write_rank    Boolean to determine if the MPI rank should be output.
+ * \param wrap_rank Number to wrap around the rank with a modulo operation.
+ *                  Can be 0 for no wrapping.
+ * \param point_scalars  Comma-separated list of point scalar fields, or NULL.
+ * \param point_vectors  Comma-separated list of point vector fields, or NULL.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p8est_vtk_write_header (p8est_t * p8est,
+                                            p8est_geometry_t * geom,
+                                            double scale,
+                                            int write_tree, int write_level,
+                                            int write_rank, int wrap_rank,
+                                            const char *point_scalars,
+                                            const char *point_vectors,
+                                            const char *filename);
+
+/** This will write a scalar field to the vtu file.
+ *
+ * It is good practice to make sure that the scalar field also
+ * exists in the comma separated string \a point_scalars passed
+ * to \c p8est_vtk_write_header.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of fields.
+ *
+ * \param p8est     The p8est to be written.
+ * \param geom      A p8est_geometry_t structure or NULL for vertex space.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ * \param scalar_name The name of the scalar field.
+ * \param values    The point values that will be written.
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p8est_vtk_write_point_scalar (p8est_t * p8est,
+                                                  p8est_geometry_t * geom,
+                                                  const char *filename,
+                                                  const char *scalar_name,
+                                                  const double *values);
+
+/** This will write a 3-vector field to the vtu file.
+ *
+ * It is good practice to make sure that the vector field also
+ * exists in the comma separated string \a point_vectors passed
+ * to \c p8est_vtk_write_header.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of fields.
+ *
+ * \param p8est     The p8est to be written.
+ * \param geom      A p8est_geometry_t structure or NULL for vertex space.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ * \param vector_name The name of the vector field.
+ * \param values    The point values that will be written.
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p8est_vtk_write_point_vector (p8est_t * p8est,
+                                                  p8est_geometry_t * geom,
+                                                  const char *filename,
+                                                  const char *vector_name,
+                                                  const double *values);
+
+/** This will write the footer of the vtu file.
+ *
+ * Writing a VTK file is split into a couple of routines.
+ * The allows there to be an arbitrary number of
+ * fields.  To write out two fields the
+ * calling sequence would be something like
+ *
+ * \begincode
+ * p8est_vtk_write_header(p8est, ..., "output");
+ * p8est_vtk_write_footer(p8est, "output");
+ * \endcode
+ *
+ * \param p8est     The p8est to be written.
+ * \param filename  The first part of the name which will have
+ *                  the proc number appended to it (i.e., the
+ *                  output file will be filename_procNum.vtu).
+ *
+ * \return          This returns 0 if no error and -1 if there is an error.
+ */
+int                 p8est_vtk_write_footer (p8est_t * p8est,
+                                            const char *filename);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_VTK_H */
diff --git a/src/p8est_wrap.c b/src/p8est_wrap.c
new file mode 100644
index 0000000..6cab255
--- /dev/null
+++ b/src/p8est_wrap.c
@@ -0,0 +1,24 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "p4est_wrap.c"
diff --git a/src/p8est_wrap.h b/src/p8est_wrap.h
new file mode 100644
index 0000000..7555d74
--- /dev/null
+++ b/src/p8est_wrap.h
@@ -0,0 +1,184 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P8EST_WRAP_H
+#define P8EST_WRAP_H
+
+#include <p8est_mesh.h>
+
+SC_EXTERN_C_BEGIN;
+
+/*** COMPLETE INTERNAL STATE OF P4EST ***/
+
+typedef enum p8est_wrap_flags
+{
+  P8EST_WRAP_NONE = 0,
+  P8EST_WRAP_REFINE = 0x01,
+  P8EST_WRAP_COARSEN = 0x02
+}
+p8est_wrap_flags_t;
+
+typedef struct p8est_wrap
+{
+  /* this member is never used or changed by p8est_wrap */
+  void               *user_pointer;     /**< Convenience member for users */
+
+  /* these members are considered public and read-only */
+  int                 p4est_dim;
+  int                 p4est_half;
+  int                 p4est_faces;
+  int                 p4est_children;
+  p8est_connectivity_t *conn;
+  p8est_t            *p4est;    /**< p4est->user_pointer is used internally */
+
+  /* anything below here is considered private und should not be touched */
+  int                 weight_exponent;
+  uint8_t            *flags, *temp_flags;
+  p4est_locidx_t      num_refine_flags, inside_counter, num_replaced;
+
+  /* for ghost and mesh use p4est_wrap_get_ghost, _mesh declared below */
+  p8est_ghost_t      *ghost;
+  p8est_mesh_t       *mesh;
+  p8est_ghost_t      *ghost_aux;
+  p8est_mesh_t       *mesh_aux;
+  int                 match_aux;
+}
+p8est_wrap_t;
+
+/** Create a p4est wrapper from a given connectivity structure.
+ * \param [in] mpicomm        We expect sc_MPI_Init to be called already.
+ * \param [in] conn           Connectivity structure.  Wrap takes ownership.
+ * \param [in] initial_level  Initial level of uniform refinement.
+ * \return                    A fully initialized p8est_wrap structure.
+ */
+p8est_wrap_t       *p8est_wrap_new_conn (sc_MPI_Comm mpicomm,
+                                         p8est_connectivity_t * conn,
+                                         int initial_level);
+
+/** Create p8est and auxiliary data structures.
+ * Expects sc_MPI_Init to be called beforehand.
+ */
+p8est_wrap_t       *p8est_wrap_new_unitcube (sc_MPI_Comm mpicomm,
+                                             int initial_level);
+p8est_wrap_t       *p8est_wrap_new_rotwrap (sc_MPI_Comm mpicomm,
+                                            int initial_level);
+
+/** Passes sc_MPI_COMM_WORLD to p8est_wrap_new_unitcube. */
+p8est_wrap_t       *p8est_wrap_new_world (int initial_level);
+void                p8est_wrap_destroy (p8est_wrap_t * pp);
+
+/** Return the appropriate ghost layer.
+ * This function is necessary since two versions may exist simultaneously
+ * after refinement and before partition/complete.
+ * */
+p8est_ghost_t      *p8est_wrap_get_ghost (p8est_wrap_t * pp);
+
+/** Return the appropriate mesh structure.
+ * This function is necessary since two versions may exist simultaneously
+ * after refinement and before partition/complete.
+ * */
+p8est_mesh_t       *p8est_wrap_get_mesh (p8est_wrap_t * pp);
+
+/** Mark a local element for refinement.
+ * This will cancel any coarsening mark set previously for this element.
+ * \param [in,out] wrap The p8est wrapper to work with.
+ * \param [in] which_tree The number of the tree this element lives in.
+ * \param [in] which_quad The number of this element relative to its tree.
+ */
+void                p8est_wrap_mark_refine (p8est_wrap_t * pp,
+                                            p4est_topidx_t which_tree,
+                                            p4est_locidx_t which_quad);
+
+/** Mark a local element for coarsening.
+ * This will cancel any refinement mark set previously for this element.
+ * \param [in,out] wrap The p8est wrapper to work with.
+ * \param [in] which_tree The number of the tree this element lives in.
+ * \param [in] which_quad The number of this element relative to its tree.
+ */
+void                p8est_wrap_mark_coarsen (p8est_wrap_t * pp,
+                                             p4est_topidx_t which_tree,
+                                             p4est_locidx_t which_quad);
+
+/** Call p8est_refine, coarsen, and balance to update pp->p8est.
+ * Checks pp->flags as per-quadrant input against p8est_wrap_flags_t.
+ * The pp->flags array is updated along with p8est and reset to zeros.
+ * Creates ghost_aux and mesh_aux to represent the intermediate mesh.
+ * \return          boolean whether p8est has changed.
+ *                  If true, partition must be called.
+ *                  If false, partition must not be called,
+ *                  and complete must not be called either.
+ */
+int                 p8est_wrap_adapt (p8est_wrap_t * pp);
+
+/** Call p8est_partition for equal leaf distribution.
+ * Frees the old ghost and mesh first and updates pp->flags along with p8est.
+ * Creates ghost and mesh to represent the new mesh.
+ * \param [in] weight_exponent      Integer weight assigned to each leaf
+ *                  according to 2 ** (level * exponent).  Passing 0 assigns
+ *                  equal weight to all leaves.  Passing 1 increases the
+ *                  leaf weight by a factor of two for each level increase.
+ *                  CURRENTLY ONLY 0 AND 1 ARE LEGAL VALUES.
+ * \return          boolean whether p4est has changed.
+ *                  If true, complete must be called.
+ *                  If false, complete must not be called.
+ */
+int                 p8est_wrap_partition (p8est_wrap_t * pp,
+                                          int weight_exponent);
+
+/** Free memory for the intermediate mesh.
+ * Sets mesh_aux and ghost_aux to NULL.
+ * This function must be used if both refinement and partition effect changes.
+ * After this call, we are ready for another mark-refine-partition cycle.
+ */
+void                p8est_wrap_complete (p8est_wrap_t * pp);
+
+/*** ITERATOR OVER THE FOREST LEAVES ***/
+
+typedef struct p8est_wrap_leaf
+{
+  p8est_wrap_t       *pp;
+  int                 level;
+  p4est_topidx_t      which_tree;
+  p4est_locidx_t      which_quad;
+  p4est_locidx_t      total_quad;
+  p8est_tree_t       *tree;
+  p8est_quadrant_t   *quad;
+  double              lowerleft[3];
+  double              upperright[3];
+}
+p8est_wrap_leaf_t;
+
+/* Create an iterator over the leaves in the forest.
+ * Returns a newly allocated state containing the first leaf,
+ * or NULL if the local partition of the tree is empty.
+ */
+p8est_wrap_leaf_t  *p8est_wrap_leaf_first (p8est_wrap_t * pp);
+
+/* Move the forest leaf iterator forward.
+ * Returns the state that was input with information for the next leaf,
+ * or NULL and deallocates the input if called with the last leaf.
+ */
+p8est_wrap_leaf_t  *p8est_wrap_leaf_next (p8est_wrap_leaf_t * leaf);
+
+SC_EXTERN_C_END;
+
+#endif /* !P8EST_WRAP_H */
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..31f84fe
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,160 @@
+
+# This file is part of p4est.
+# Makefile.am test
+# included non-recursively from toplevel directory
+
+p4est_test_programs =
+if P4EST_ENABLE_BUILD_2D
+p4est_test_programs += \
+        test/p4est_test_comm test/p4est_test_hash \
+        test/p4est_test_quadrants test/p4est_test_balance \
+        test/p4est_test_partition test/p4est_test_coarsen \
+        test/p4est_test_valid test/p4est_test_balance_type \
+        test/p4est_test_loadsave test/p4est_test_order \
+        test/p4est_test_ghost \
+        test/p4est_test_iterate test/p4est_test_lnodes \
+        test/p4est_test_search test/p4est_test_brick \
+        test/p4est_test_partition_corr \
+        test/p4est_test_conn_complete test/p4est_test_balance_seeds \
+        test/p4est_test_wrap test/p4est_test_replace test/p4est_test_join \
+        test/p4est_test_conn_reduce test/p4est_test_plex
+if P4EST_WITH_METIS
+p4est_test_programs += \
+        test/p4est_test_reorder
+endif
+endif
+if P4EST_ENABLE_BUILD_3D
+p4est_test_programs += \
+        test/p8est_test_quadrants test/p8est_test_balance \
+        test/p8est_test_partition test/p8est_test_coarsen \
+        test/p8est_test_valid test/p8est_test_balance_type \
+        test/p8est_test_face_transform test/p8est_test_edge_face_corners \
+        test/p8est_test_periodic test/p8est_test_loadsave \
+        test/p8est_test_ghost \
+        test/p8est_test_iterate test/p8est_test_lnodes \
+        test/p8est_test_search test/p8est_test_brick \
+        test/p8est_test_partition_corr \
+        test/p8est_test_conn_complete test/p8est_test_balance_seeds \
+        test/p8est_test_wrap test/p8est_test_replace test/p8est_test_join \
+        test/p8est_test_conn_reduce test/p8est_test_plex
+if P4EST_WITH_METIS
+p4est_test_programs += \
+        test/p8est_test_reorder
+endif
+endif
+
+check_PROGRAMS += $(p4est_test_programs)
+
+test_p4est_test_comm_SOURCES = test/test_comm.c
+test_p4est_test_hash_SOURCES = test/test_hash.c
+test_p4est_test_quadrants_SOURCES = test/test_quadrants2.c
+test_p4est_test_balance_SOURCES = test/test_balance2.c
+test_p4est_test_partition_SOURCES = test/test_partition2.c
+test_p4est_test_order_SOURCES = test/test_order.c
+test_p4est_test_coarsen_SOURCES = test/test_coarsen2.c
+test_p4est_test_valid_SOURCES = test/test_valid2.c
+test_p4est_test_balance_type_SOURCES = test/test_balance_type2.c
+test_p4est_test_loadsave_SOURCES = test/test_loadsave2.c
+test_p4est_test_ghost_SOURCES = test/test_ghost2.c
+test_p4est_test_iterate_SOURCES = test/test_iterate2.c
+test_p4est_test_lnodes_SOURCES = test/test_lnodes2.c
+test_p4est_test_search_SOURCES = test/test_search2.c
+test_p4est_test_brick_SOURCES = test/test_brick2.c
+test_p4est_test_partition_corr_SOURCES = test/test_partition_corr2.c
+test_p4est_test_conn_complete_SOURCES = test/test_conn_complete2.c
+test_p4est_test_balance_seeds_SOURCES = test/test_balance_seeds2.c
+test_p4est_test_wrap_SOURCES = test/test_wrap2.c
+test_p4est_test_replace_SOURCES = test/test_replace2.c
+test_p4est_test_join_SOURCES = test/test_join2.c
+test_p4est_test_conn_reduce_SOURCES = test/test_conn_reduce2.c
+test_p4est_test_plex_SOURCES = test/test_plex2.c
+
+test_p8est_test_quadrants_SOURCES = test/test_quadrants3.c
+test_p8est_test_balance_SOURCES = test/test_balance3.c
+test_p8est_test_partition_SOURCES = test/test_partition3.c
+test_p8est_test_coarsen_SOURCES = test/test_coarsen3.c
+test_p8est_test_valid_SOURCES = test/test_valid3.c
+test_p8est_test_balance_type_SOURCES = test/test_balance_type3.c
+test_p8est_test_face_transform_SOURCES = test/test_face_transform3.c
+test_p8est_test_edge_face_corners_SOURCES = test/test_edge_face_corners3.c
+test_p8est_test_periodic_SOURCES = test/test_periodic3.c
+test_p8est_test_loadsave_SOURCES = test/test_loadsave3.c
+test_p8est_test_ghost_SOURCES = test/test_ghost3.c
+test_p8est_test_brick_SOURCES = test/test_brick3.c
+test_p8est_test_iterate_SOURCES = test/test_iterate3.c
+test_p8est_test_lnodes_SOURCES = test/test_lnodes3.c
+test_p8est_test_search_SOURCES = test/test_search3.c
+test_p8est_test_partition_corr_SOURCES = test/test_partition_corr3.c
+test_p8est_test_conn_complete_SOURCES = test/test_conn_complete3.c
+test_p8est_test_balance_seeds_SOURCES = test/test_balance_seeds3.c
+test_p8est_test_wrap_SOURCES = test/test_wrap3.c
+test_p8est_test_replace_SOURCES = test/test_replace3.c
+test_p8est_test_join_SOURCES = test/test_join3.c
+test_p8est_test_conn_reduce_SOURCES = test/test_conn_reduce3.c
+test_p8est_test_plex_SOURCES = test/test_plex3.c
+
+test_p4est_test_plex_CPPFLAGS = @P4EST_PETSC_INCLUDE_DIRS@ $(AM_CPPFLAGS) 
+test_p8est_test_plex_CPPFLAGS = @P4EST_PETSC_INCLUDE_DIRS@ $(AM_CPPFLAGS) 
+test_p4est_test_plex_LDADD = @P4EST_PETSC_LINK_LIBS@ $(LDADD) 
+test_p8est_test_plex_LDADD = @P4EST_PETSC_LINK_LIBS@ $(LDADD) 
+
+if P4EST_WITH_METIS
+test_p4est_test_reorder_SOURCES = test/test_reorder2.c
+test_p8est_test_reorder_SOURCES = test/test_reorder3.c
+endif
+
+TESTS += $(p4est_test_programs)
+
+LINT_CSOURCES += \
+        $(test_p4est_test_comm_SOURCES) \
+        $(test_p4est_test_hash_SOURCES) \
+        $(test_p4est_test_quadrants_SOURCES) \
+        $(test_p4est_test_balance_SOURCES) \
+        $(test_p4est_test_partition_SOURCES) \
+        $(test_p4est_test_order_SOURCES) \
+        $(test_p4est_test_coarsen_SOURCES) \
+        $(test_p4est_test_valid_SOURCES) \
+        $(test_p4est_test_balance_type_SOURCES) \
+        $(test_p4est_test_loadsave_SOURCES) \
+        $(test_p4est_test_ghost_SOURCES) \
+        $(test_p4est_test_iterate_SOURCES) \
+        $(test_p4est_test_lnodes_SOURCES) \
+        $(test_p4est_test_search_SOURCES) \
+        $(test_p4est_test_brick_SOURCES) \
+        $(test_p4est_test_partition_corr_SOURCES) \
+        $(test_p4est_test_reorder_SOURCES) \
+        $(test_p4est_test_balance_seeds_SOURCES) \
+        $(test_p4est_test_wrap_SOURCES) \
+        $(test_p4est_test_replace_SOURCES) \
+        $(test_p4est_test_join_SOURCES) \
+        $(test_p4est_test_conn_reduce_SOURCES) \
+        $(test_p4est_test_plex_SOURCES) \
+        $(test_p8est_test_quadrants_SOURCES) \
+        $(test_p8est_test_balance_SOURCES) \
+        $(test_p8est_test_partition_SOURCES) \
+        $(test_p8est_test_coarsen_SOURCES) \
+        $(test_p8est_test_valid_SOURCES) \
+        $(test_p8est_test_balance_type_SOURCES) \
+        $(test_p8est_test_face_transform_SOURCES) \
+        $(test_p8est_test_edge_face_corners_SOURCES) \
+        $(test_p8est_test_periodic_SOURCES) \
+        $(test_p8est_test_loadsave_SOURCES) \
+        $(test_p8est_test_ghost_SOURCES) \
+        $(test_p8est_test_brick_SOURCES) \
+        $(test_p8est_test_iterate_SOURCES) \
+        $(test_p8est_test_lnodes_SOURCES) \
+        $(test_p8est_test_search_SOURCES) \
+        $(test_p8est_test_reorder_SOURCES) \
+        $(test_p8est_test_partition_corr_SOURCES) \
+        $(test_p8est_test_balance_seeds_SOURCES) \
+        $(test_p8est_test_wrap_SOURCES) \
+        $(test_p8est_test_replace_SOURCES) \
+        $(test_p8est_test_join_SOURCES) \
+        $(test_p8est_test_conn_reduce_SOURCES) \
+        $(test_p8est_test_plex_SOURCES)
+
+if P4EST_WITH_METIS
+LINT_CSOURCES += \
+        $(test_p4est_test_reorder_SOURCES) \
+        $(test_p8est_test_reorder_SOURCES)
+endif
diff --git a/test/test_balance2.c b/test/test_balance2.c
new file mode 100644
index 0000000..13c3a18
--- /dev/null
+++ b/test/test_balance2.c
@@ -0,0 +1,203 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_ghost.h>
+#include <p4est_nodes.h>
+#else
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_ghost.h>
+#include <p8est_nodes.h>
+#endif
+
+#ifndef P4_TO_P8
+static const int    refine_level = 5;
+#else
+static const int    refine_level = 3;
+#endif
+
+static void
+init_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+         p4est_quadrant_t * quadrant)
+{
+}
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  if (which_tree == 2 || which_tree == 3) {
+    return 0;
+  }
+
+  cid = p4est_quadrant_child_id (quadrant);
+
+  if (cid == P4EST_CHILDREN - 1 ||
+      (quadrant->x >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2) &&
+       quadrant->y >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#ifdef P4_TO_P8
+       && quadrant->z >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#endif
+      )) {
+    return 1;
+  }
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && cid == 2) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_QUADRANT_LEN (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->y >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 mpisize, mpirank;
+  unsigned            crc;
+#ifndef P4_TO_P8
+  size_t              kz;
+  int8_t              l;
+  p4est_quadrant_t   *q;
+  p4est_tree_t        stree, *tree = &stree;
+#endif
+  p4est_t            *p4est;
+  p4est_connectivity_t *connectivity;
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+#ifndef P4_TO_P8
+  connectivity = p4est_connectivity_new_star ();
+#else
+  connectivity = p8est_connectivity_new_rotcubes ();
+#endif
+  p4est = p4est_new_ext (mpicomm, connectivity, 0, 0, 0, 4, NULL, NULL);
+
+#ifndef P4_TO_P8
+  /* build empty tree */
+  sc_array_init (&tree->quadrants, sizeof (p4est_quadrant_t));
+  for (l = 0; l <= P4EST_MAXLEVEL; ++l) {
+    tree->quadrants_per_level[l] = 0;
+  }
+  tree->maxlevel = 0;
+
+  /* insert two quadrants */
+  sc_array_resize (&tree->quadrants, 4);
+  q = p4est_quadrant_array_index (&tree->quadrants, 0);
+  p4est_quadrant_set_morton (q, 3, 13);
+  q = p4est_quadrant_array_index (&tree->quadrants, 1);
+  p4est_quadrant_set_morton (q, 1, 1);
+  q = p4est_quadrant_array_index (&tree->quadrants, 2);
+  p4est_quadrant_set_morton (q, 1, 2);
+  q = p4est_quadrant_array_index (&tree->quadrants, 3);
+  p4est_quadrant_set_morton (q, 1, 3);
+  for (kz = 0; kz < tree->quadrants.elem_count; ++kz) {
+    q = p4est_quadrant_array_index (&tree->quadrants, kz);
+    q->p.user_data = sc_mempool_alloc (p4est->user_data_pool);
+    ++tree->quadrants_per_level[q->level];
+    tree->maxlevel = (int8_t) SC_MAX (tree->maxlevel, q->level);
+  }
+
+  /* balance the tree, print and destroy */
+#if 0
+  p4est_balance_subtree (p4est, P4EST_CONNECT_FULL, 0, NULL);
+  p4est_tree_print (SC_LP_INFO, tree);
+#endif
+  for (kz = 0; kz < tree->quadrants.elem_count; ++kz) {
+    q = p4est_quadrant_array_index (&tree->quadrants, kz);
+    sc_mempool_free (p4est->user_data_pool, q->p.user_data);
+  }
+  sc_array_reset (&tree->quadrants);
+#endif /* !P4_TO_P8 */
+
+  /* check reset data function */
+  p4est_reset_data (p4est, 0, init_fn, NULL);
+  p4est_reset_data (p4est, 0, NULL, NULL);
+
+  /* refine and balance the forest */
+  SC_CHECK_ABORT (p4est_is_balanced (p4est, P4EST_CONNECT_FULL), "Balance 1");
+  p4est_refine (p4est, 1, refine_fn, NULL);
+  SC_CHECK_ABORT (!p4est_is_balanced (p4est, P4EST_CONNECT_FULL),
+                  "Balance 2");
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+  SC_CHECK_ABORT (p4est_is_balanced (p4est, P4EST_CONNECT_FULL), "Balance 3");
+
+  /* check reset data function */
+  p4est_reset_data (p4est, 17, NULL, NULL);
+  p4est_reset_data (p4est, 8, init_fn, NULL);
+
+  /* checksum and partition */
+  crc = p4est_checksum (p4est);
+  p4est_partition (p4est, 0, NULL);
+  SC_CHECK_ABORT (p4est_checksum (p4est) == crc, "Partition");
+  SC_CHECK_ABORT (p4est_is_balanced (p4est, P4EST_CONNECT_FULL), "Balance 4");
+
+  /* check reset data function */
+  p4est_reset_data (p4est, 3, NULL, NULL);
+  p4est_reset_data (p4est, 3, NULL, NULL);
+
+  /* checksum and rebalance */
+  crc = p4est_checksum (p4est);
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+  SC_CHECK_ABORT (p4est_checksum (p4est) == crc, "Rebalance");
+
+  /* clean up and exit */
+  P4EST_ASSERT (p4est->user_data_pool->elem_count ==
+                (size_t) p4est->local_num_quadrants);
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (connectivity);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_balance3.c b/test/test_balance3.c
new file mode 100644
index 0000000..0f71eac
--- /dev/null
+++ b/test/test_balance3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_balance2.c"
diff --git a/test/test_balance_seeds2.c b/test/test_balance_seeds2.c
new file mode 100644
index 0000000..4bede93
--- /dev/null
+++ b/test/test_balance_seeds2.c
@@ -0,0 +1,639 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2011 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_balance.h>
+#include <p4est_bits.h>
+#else
+#include <p8est_balance.h>
+#include <p8est_bits.h>
+#endif
+
+static int
+is_farther (p4est_quadrant_t * orig, p4est_quadrant_t * targ,
+            p4est_quadrant_t * newq)
+{
+  p4est_qcoord_t      ox1, ox2, nx1, nx2;
+  p4est_qcoord_t      oy1, oy2, ny1, ny2;
+#ifdef P4_TO_P8
+  p4est_qcoord_t      oz1, oz2, nz1, nz2;
+#endif
+  p4est_qcoord_t      nl = P4EST_QUADRANT_LEN (newq->level);
+  p4est_qcoord_t      tl = P4EST_QUADRANT_LEN (targ->level);
+
+  P4EST_ASSERT (newq->level == orig->level);
+
+  ox1 = targ->x - orig->x;
+  ox2 = (orig->x + nl) - (targ->x + tl);
+  nx1 = targ->x - newq->x;
+  nx2 = (newq->x + nl) - (targ->x + tl);
+  if (ox1 > 0 && nx1 > ox1) {
+    return 1;
+  }
+  if (ox2 > 0 && nx2 > ox2) {
+    return 1;
+  }
+
+  oy1 = targ->y - orig->y;
+  oy2 = (orig->y + nl) - (targ->y + tl);
+  ny1 = targ->y - newq->y;
+  ny2 = (newq->y + nl) - (targ->y + tl);
+  if (oy1 > 0 && ny1 > oy1) {
+    return 1;
+  }
+  if (oy2 > 0 && ny2 > oy2) {
+    return 1;
+  }
+
+#ifdef P4_TO_P8
+  oz1 = targ->z - orig->z;
+  oz2 = (orig->z + nl) - (targ->z + tl);
+  nz1 = targ->z - newq->z;
+  nz2 = (newq->z + nl) - (targ->z + tl);
+  if (oz1 > 0 && nz1 > oz1) {
+    return 1;
+  }
+  if (oz2 > 0 && nz2 > oz2) {
+    return 1;
+  }
+#endif
+
+  return 0;
+
+}
+
+int
+check_balance_seeds (p4est_quadrant_t * q, p4est_quadrant_t * p,
+                     p4est_connect_type_t b, sc_array_t * seeds)
+{
+  int                 ib;
+  int                 level = q->level;
+  p4est_quadrant_t   *s, *t;
+  sc_array_t         *thislevel = sc_array_new (sizeof (p4est_quadrant_t));
+  sc_array_t         *nextlevel = sc_array_new (sizeof (p4est_quadrant_t));
+  sc_array_t         *temparray;
+  p4est_quadrant_t    temp1, temp2;
+  int                 f, c;
+#ifdef P4_TO_P8
+  int                 e;
+#endif
+  int                 stop = 0;
+
+  sc_array_resize (seeds, 0);
+
+  s = (p4est_quadrant_t *) sc_array_push (thislevel);
+  p4est_quadrant_sibling (q, s, 0);
+
+#ifndef P4_TO_P8
+  if (b == P4EST_CONNECT_FACE) {
+    ib = 0;
+  }
+  else {
+    ib = 1;
+  }
+#else
+  if (b == P8EST_CONNECT_FACE) {
+    ib = 0;
+  }
+  else if (b == P8EST_CONNECT_EDGE) {
+    ib = 1;
+  }
+  else {
+    ib = 2;
+  }
+#endif
+
+  while (level > p->level + 1) {
+    size_t              nlast = thislevel->elem_count;
+    size_t              zz;
+
+    stop = 0;
+
+    for (zz = 0; zz < nlast; zz++) {
+      s = p4est_quadrant_array_index (thislevel, zz);
+      P4EST_ASSERT (p4est_quadrant_child_id (s) == 0);
+      p4est_quadrant_parent (s, &temp1);
+      for (f = 0; f < P4EST_FACES; f++) {
+        p4est_quadrant_face_neighbor (&temp1, f, &temp2);
+        if (is_farther (&temp1, p, &temp2)) {
+          continue;
+        }
+        if (p4est_quadrant_is_ancestor (p, &temp2)) {
+          stop = 1;
+          sc_array_resize (seeds, seeds->elem_count + 1);
+          t = p4est_quadrant_array_index (seeds, seeds->elem_count - 1);
+          p4est_quadrant_sibling (&temp2, t, 0);
+        }
+        else if (p4est_quadrant_is_inside_root (&temp2)) {
+          t = (p4est_quadrant_t *) sc_array_push (nextlevel);
+          p4est_quadrant_sibling (&temp2, t, 0);
+        }
+      }
+
+      if (ib == 0) {
+        continue;
+      }
+
+#ifdef P4_TO_P8
+      for (e = 0; e < P8EST_EDGES; e++) {
+        p8est_quadrant_edge_neighbor (&temp1, e, &temp2);
+        if (is_farther (&temp1, p, &temp2)) {
+          continue;
+        }
+        if (p4est_quadrant_is_ancestor (p, &temp2)) {
+          stop = 1;
+          sc_array_resize (seeds, seeds->elem_count + 1);
+          t = p4est_quadrant_array_index (seeds, seeds->elem_count - 1);
+          p4est_quadrant_sibling (&temp2, t, 0);
+        }
+        else if (p4est_quadrant_is_inside_root (&temp2)) {
+          t = (p4est_quadrant_t *) sc_array_push (nextlevel);
+          p4est_quadrant_sibling (&temp2, t, 0);
+        }
+      }
+
+      if (ib == 1) {
+        continue;
+      }
+#endif
+
+      for (c = 0; c < P4EST_CHILDREN; c++) {
+        p4est_quadrant_corner_neighbor (&temp1, c, &temp2);
+        if (is_farther (&temp1, p, &temp2)) {
+          continue;
+        }
+        if (p4est_quadrant_is_ancestor (p, &temp2)) {
+          stop = 1;
+          sc_array_resize (seeds, seeds->elem_count + 1);
+          t = p4est_quadrant_array_index (seeds, seeds->elem_count - 1);
+          p4est_quadrant_sibling (&temp2, t, 0);
+        }
+        else if (p4est_quadrant_is_inside_root (&temp2)) {
+          t = (p4est_quadrant_t *) sc_array_push (nextlevel);
+          p4est_quadrant_sibling (&temp2, t, 0);
+        }
+      }
+    }
+
+    if (stop) {
+      sc_array_sort (seeds, p4est_quadrant_compare);
+      sc_array_uniq (seeds, p4est_quadrant_compare);
+
+#ifdef P4_TO_P8
+      if (!ib && seeds->elem_count == 1) {
+        sc_array_sort (nextlevel, p4est_quadrant_compare);
+        sc_array_uniq (nextlevel, p4est_quadrant_compare);
+        temparray = thislevel;
+        thislevel = nextlevel;
+        nextlevel = temparray;
+        sc_array_reset (nextlevel);
+        level--;
+
+        nlast = thislevel->elem_count;
+        for (zz = 0; zz < nlast; zz++) {
+          s = p4est_quadrant_array_index (thislevel, zz);
+          P4EST_ASSERT (p4est_quadrant_child_id (s) == 0);
+          p4est_quadrant_parent (s, &temp1);
+          for (f = 0; f < P4EST_FACES; f++) {
+            p4est_quadrant_face_neighbor (&temp1, f, &temp2);
+            if (p4est_quadrant_is_ancestor (p, &temp2)) {
+              int                 f2;
+              p4est_quadrant_t    a;
+              p4est_quadrant_t    u;
+
+              t = p4est_quadrant_array_index (seeds, 0);
+
+              p8est_quadrant_parent (t, &a);
+
+              for (f2 = 0; f2 < P8EST_FACES; f2++) {
+                if (f2 / 2 == f / 2) {
+                  continue;
+                }
+                p8est_quadrant_face_neighbor (&a, f2, &u);
+
+                if (p8est_quadrant_is_equal (&temp2, &u) ||
+                    p8est_quadrant_is_sibling (&temp2, &u)) {
+                  break;
+                }
+              }
+
+              if (f2 == P8EST_FACES) {
+                sc_array_resize (seeds, seeds->elem_count + 1);
+                t = p4est_quadrant_array_index (seeds, seeds->elem_count - 1);
+                p4est_quadrant_sibling (&temp2, t, 0);
+              }
+            }
+          }
+        }
+      }
+#endif
+      sc_array_sort (seeds, p4est_quadrant_compare);
+      sc_array_uniq (seeds, p4est_quadrant_compare);
+
+      break;
+    }
+    sc_array_sort (nextlevel, p4est_quadrant_compare);
+    sc_array_uniq (nextlevel, p4est_quadrant_compare);
+    temparray = thislevel;
+    thislevel = nextlevel;
+    nextlevel = temparray;
+    sc_array_reset (nextlevel);
+    level--;
+  }
+
+  sc_array_destroy (thislevel);
+  sc_array_destroy (nextlevel);
+
+  return stop;
+}
+
+void
+standard_seeds (sc_array_t * seeds)
+{
+  size_t              count = seeds->elem_count;
+  size_t              zz;
+  p4est_quadrant_t   *q, temp;
+
+  for (zz = 0; zz < count; zz++) {
+    q = p4est_quadrant_array_index (seeds, zz);
+    p4est_quadrant_sibling (q, &temp, 0);
+    *q = temp;
+  }
+  sc_array_sort (seeds, p4est_quadrant_compare);
+  sc_array_uniq (seeds, p4est_quadrant_compare);
+}
+
+void
+compare_seeds (sc_array_t * seeds, sc_array_t * seeds_check)
+{
+  size_t              count = seeds->elem_count;
+  size_t              zz;
+  p4est_quadrant_t   *s, *t;
+
+  SC_CHECK_ABORT (seeds_check->elem_count == count, "seed count");
+
+  for (zz = 0; zz < count; zz++) {
+    s = p4est_quadrant_array_index (seeds, zz);
+    t = p4est_quadrant_array_index (seeds_check, zz);
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (s, t), "seed equality");
+  }
+}
+
+int
+main (int argc, char **argv)
+{
+  p4est_quadrant_t    root;
+  p4est_quadrant_t    p;
+  p4est_quadrant_t    q;
+  p4est_quadrant_t    desc;
+  int                 face, corner;
+#ifndef P4_TO_P8
+  int                 maxlevel = 9;
+#else
+  int                 edge;
+  int                 maxlevel = 6;
+#endif
+  int                 mpiret, mpisize, mpirank;
+  sc_MPI_Comm         mpicomm;
+  uint64_t            i, ifirst, ilast;
+  int                 level;
+  sc_array_t         *seeds, *seeds_check;
+  int                 testval;
+  int                 checkval;
+  int                 j, nrand = 1000;
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  srandom (9212007);
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  seeds = sc_array_new (sizeof (p4est_quadrant_t));
+  seeds_check = sc_array_new (sizeof (p4est_quadrant_t));
+
+  memset (&root, 0, sizeof (p4est_quadrant_t));
+  root.level = 2;
+  root.x = P4EST_QUADRANT_LEN (2);
+  root.y = P4EST_QUADRANT_LEN (2);
+#ifdef P4_TO_P8
+  root.z = P4EST_QUADRANT_LEN (2);
+#endif
+  P4EST_QUADRANT_INIT (&p);
+  P4EST_QUADRANT_INIT (&q);
+
+#if 1
+  for (face = 0; face < P4EST_FACES; face++) {
+    p4est_quadrant_face_neighbor (&root, face ^ 1, &p);
+    P4EST_GLOBAL_VERBOSEF ("Testing face %d\n", face);
+    for (level = 4; level <= maxlevel; level++) {
+      P4EST_GLOBAL_VERBOSEF (" level %d\n", level);
+      p4est_quadrant_first_descendant (&root, &desc, level);
+      ifirst = p4est_quadrant_linear_id (&desc, level);
+      p4est_quadrant_last_descendant (&root, &desc, level);
+      ilast = p4est_quadrant_linear_id (&desc, level);
+      for (i = ifirst; i <= ilast; i += P4EST_CHILDREN) {
+        p4est_quadrant_set_morton (&q, level, i);
+#ifndef P4_TO_P8
+        testval = p4est_balance_seeds_face (&q, &p, face, P4EST_CONNECT_FACE,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P4EST_CONNECT_FACE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p4est_balance_seeds_face error");
+        compare_seeds (seeds, seeds_check);
+#else
+        testval = p4est_balance_seeds_face (&q, &p, face, P8EST_CONNECT_FACE,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_FACE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_face error");
+        compare_seeds (seeds, seeds_check);
+        testval = p4est_balance_seeds_face (&q, &p, face, P8EST_CONNECT_EDGE,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_EDGE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_face error");
+        compare_seeds (seeds, seeds_check);
+#endif
+        testval = p4est_balance_seeds_face (&q, &p, face, P4EST_CONNECT_FULL,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P4EST_CONNECT_FULL,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p4est_balance_seeds_face error");
+        compare_seeds (seeds, seeds_check);
+      }
+    }
+    if (!face) {
+      P4EST_GLOBAL_VERBOSE (" random levels\n");
+      for (j = 0; j < (int) nrand; j++) {
+        level = ((random ()) % (P4EST_QMAXLEVEL - maxlevel)) + maxlevel + 1;
+        p4est_quadrant_first_descendant (&root, &desc, level);
+        ifirst = p4est_quadrant_linear_id (&desc, level);
+        p4est_quadrant_last_descendant (&root, &desc, level);
+        ilast = p4est_quadrant_linear_id (&desc, level);
+        i = ((random ()) % (ilast + 1 - ifirst)) + ifirst;
+        p4est_quadrant_set_morton (&q, level, i);
+#ifndef P4_TO_P8
+        testval = p4est_balance_seeds_face (&q, &p, face, P4EST_CONNECT_FACE,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P4EST_CONNECT_FACE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p4est_balance_seeds_face error");
+        compare_seeds (seeds, seeds_check);
+#else
+        testval = p4est_balance_seeds_face (&q, &p, face, P8EST_CONNECT_FACE,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_FACE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_face error");
+        compare_seeds (seeds, seeds_check);
+        testval = p4est_balance_seeds_face (&q, &p, face, P8EST_CONNECT_EDGE,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_EDGE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_face error");
+        compare_seeds (seeds, seeds_check);
+#endif
+        testval = p4est_balance_seeds_face (&q, &p, face, P4EST_CONNECT_FULL,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P4EST_CONNECT_FULL,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p4est_balance_seeds_face error");
+        compare_seeds (seeds, seeds_check);
+      }
+    }
+  }
+
+#ifdef P4_TO_P8
+  for (edge = 0; edge < P8EST_EDGES; edge++) {
+    p8est_quadrant_edge_neighbor (&root, edge ^ 3, &p);
+    P4EST_GLOBAL_VERBOSEF ("Testing edge %d\n", edge);
+    for (level = 4; level <= maxlevel; level++) {
+      P4EST_GLOBAL_VERBOSEF (" level %d\n", level);
+      p4est_quadrant_first_descendant (&root, &desc, level);
+      ifirst = p4est_quadrant_linear_id (&desc, level);
+      p4est_quadrant_last_descendant (&root, &desc, level);
+      ilast = p4est_quadrant_linear_id (&desc, level);
+      for (i = ifirst; i <= ilast; i += P4EST_CHILDREN) {
+        p4est_quadrant_set_morton (&q, level, i);
+        testval = p8est_balance_seeds_edge (&q, &p, edge, P8EST_CONNECT_FACE,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_FACE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_edge error");
+        compare_seeds (seeds, seeds_check);
+        testval = p8est_balance_seeds_edge (&q, &p, edge, P8EST_CONNECT_EDGE,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_EDGE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_edge error");
+        compare_seeds (seeds, seeds_check);
+        testval = p8est_balance_seeds_edge (&q, &p, edge, P8EST_CONNECT_FULL,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_FULL,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_edge error");
+        compare_seeds (seeds, seeds_check);
+      }
+    }
+    if (!edge) {
+      P4EST_GLOBAL_VERBOSE (" random levels\n");
+      for (j = 0; j < (int) nrand; j++) {
+        level = ((random ()) % (P4EST_QMAXLEVEL - maxlevel)) + maxlevel + 1;
+        p4est_quadrant_first_descendant (&root, &desc, level);
+        ifirst = p4est_quadrant_linear_id (&desc, level);
+        p4est_quadrant_last_descendant (&root, &desc, level);
+        ilast = p4est_quadrant_linear_id (&desc, level);
+        i = ((random ()) % (ilast + 1 - ifirst)) + ifirst;
+        p4est_quadrant_set_morton (&q, level, i);
+        testval = p8est_balance_seeds_edge (&q, &p, edge, P8EST_CONNECT_FACE,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_FACE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_edge error");
+        compare_seeds (seeds, seeds_check);
+        testval = p8est_balance_seeds_edge (&q, &p, edge, P8EST_CONNECT_EDGE,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_EDGE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_edge error");
+        compare_seeds (seeds, seeds_check);
+        testval = p8est_balance_seeds_edge (&q, &p, edge, P8EST_CONNECT_FULL,
+                                            seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_FULL,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_edge error");
+        compare_seeds (seeds, seeds_check);
+      }
+    }
+  }
+#endif
+#endif
+
+  for (corner = 0; corner < P4EST_FACES; corner++) {
+    p4est_quadrant_corner_neighbor (&root, corner ^ (P4EST_CHILDREN - 1), &p);
+    P4EST_GLOBAL_VERBOSEF ("Testing corner %d\n", corner);
+    for (level = 4; level <= maxlevel; level++) {
+      P4EST_GLOBAL_VERBOSEF (" level %d\n", level);
+      p4est_quadrant_first_descendant (&root, &desc, level);
+      ifirst = p4est_quadrant_linear_id (&desc, level);
+      p4est_quadrant_last_descendant (&root, &desc, level);
+      ilast = p4est_quadrant_linear_id (&desc, level);
+      for (i = ifirst; i <= ilast; i += P4EST_CHILDREN) {
+        p4est_quadrant_set_morton (&q, level, i);
+#ifndef P4_TO_P8
+        testval =
+          p4est_balance_seeds_corner (&q, &p, corner, P4EST_CONNECT_FACE,
+                                      seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P4EST_CONNECT_FACE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p4est_balance_seeds_corner error");
+        compare_seeds (seeds, seeds_check);
+#else
+        testval = p4est_balance_seeds_corner (&q, &p, corner,
+                                              P8EST_CONNECT_FACE, seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_FACE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_corner error");
+        compare_seeds (seeds, seeds_check);
+        testval =
+          p4est_balance_seeds_corner (&q, &p, corner, P8EST_CONNECT_EDGE,
+                                      seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_EDGE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_corner error");
+        compare_seeds (seeds, seeds_check);
+#endif
+        testval =
+          p4est_balance_seeds_corner (&q, &p, corner, P4EST_CONNECT_FULL,
+                                      seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P4EST_CONNECT_FULL,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p4est_balance_seeds_corner error");
+        compare_seeds (seeds, seeds_check);
+      }
+    }
+    if (!corner) {
+      P4EST_GLOBAL_VERBOSE (" random levels\n");
+      for (j = 0; j < (int) nrand; j++) {
+        level = ((random ()) % (P4EST_QMAXLEVEL - maxlevel)) + maxlevel + 1;
+        p4est_quadrant_first_descendant (&root, &desc, level);
+        ifirst = p4est_quadrant_linear_id (&desc, level);
+        p4est_quadrant_last_descendant (&root, &desc, level);
+        ilast = p4est_quadrant_linear_id (&desc, level);
+        i = ((random ()) % (ilast + 1 - ifirst)) + ifirst;
+        p4est_quadrant_set_morton (&q, level, i);
+#ifndef P4_TO_P8
+        testval =
+          p4est_balance_seeds_corner (&q, &p, corner, P4EST_CONNECT_FACE,
+                                      seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P4EST_CONNECT_FACE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p4est_balance_seeds_corner error");
+        compare_seeds (seeds, seeds_check);
+#else
+        testval = p4est_balance_seeds_corner (&q, &p, corner,
+                                              P8EST_CONNECT_FACE, seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_FACE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_corner error");
+        compare_seeds (seeds, seeds_check);
+        testval =
+          p4est_balance_seeds_corner (&q, &p, corner, P8EST_CONNECT_EDGE,
+                                      seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P8EST_CONNECT_EDGE,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p8est_balance_seeds_corner error");
+        compare_seeds (seeds, seeds_check);
+#endif
+        testval =
+          p4est_balance_seeds_corner (&q, &p, corner, P4EST_CONNECT_FULL,
+                                      seeds);
+        standard_seeds (seeds);
+        checkval = check_balance_seeds (&q, &p, P4EST_CONNECT_FULL,
+                                        seeds_check);
+        SC_CHECK_ABORT (testval == checkval,
+                        "p4est_balance_seeds_corner error");
+        compare_seeds (seeds, seeds_check);
+      }
+    }
+  }
+
+  sc_array_destroy (seeds);
+  sc_array_destroy (seeds_check);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_balance_seeds3.c b/test/test_balance_seeds3.c
new file mode 100644
index 0000000..6d73bfc
--- /dev/null
+++ b/test/test_balance_seeds3.c
@@ -0,0 +1,27 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2011 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_balance_seeds2.c"
+
+/* EOF test_balance_seeds3.c */
diff --git a/test/test_balance_type2.c b/test/test_balance_type2.c
new file mode 100644
index 0000000..f261196
--- /dev/null
+++ b/test/test_balance_type2.c
@@ -0,0 +1,167 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_ghost.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_ghost.h>
+#endif
+
+#ifndef P4_TO_P8
+static const int    refine_level = 5;
+#else
+static const int    refine_level = 3;
+#endif
+static const int    ueber_level = 10;
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  if (which_tree == 2 || which_tree == 3) {
+    return 0;
+  }
+
+  cid = p4est_quadrant_child_id (quadrant);
+
+  if (cid == P4EST_CHILDREN - 1 ||
+      (quadrant->x >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2) &&
+       quadrant->y >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#ifdef P4_TO_P8
+       && quadrant->z >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#endif
+      )) {
+    return (int) quadrant->level < ueber_level;
+  }
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && cid == 2) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_QUADRANT_LEN (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->y >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 size, rank;
+  unsigned            crcF, crcC;
+  p4est_connectivity_t *connectivity;
+  p4est_t            *p4est;
+  p4est_t            *p4estF, *p4estC;
+#ifdef P4_TO_P8
+  unsigned            crcE;
+  p4est_t            *p4estE;
+#endif
+
+  /* initialize */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &size);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* create forest and refine */
+#ifndef P4_TO_P8
+  connectivity = p4est_connectivity_new_star ();
+#else
+  connectivity = p8est_connectivity_new_rotcubes ();
+#endif
+  p4est = p4est_new_ext (mpicomm, connectivity, 0, 0, 0, 0, NULL, NULL);
+  p4est_refine (p4est, 1, refine_fn, NULL);
+
+  /* test face balance */
+  p4estF = p4est_copy (p4est, 0);
+#ifndef P4_TO_P8
+  p4est_balance (p4estF, P4EST_CONNECT_FACE, NULL);
+#else
+  p4est_balance (p4estF, P8EST_CONNECT_FACE, NULL);
+#endif
+  crcF = p4est_checksum (p4estF);
+  P4EST_GLOBAL_INFOF ("Face balance with %lld quadrants and crc 0x%08x\n",
+                      (long long) p4estF->global_num_quadrants, crcF);
+
+#ifdef P4_TO_P8
+  /* test edge balance */
+  p4estE = p4est_copy (p4est, 1);
+  p4est_balance (p4estF, P8EST_CONNECT_EDGE, NULL);
+  p4est_balance (p4estE, P8EST_CONNECT_EDGE, NULL);
+  crcE = p4est_checksum (p4estE);
+  SC_CHECK_ABORT (crcE == p4est_checksum (p4estF), "mismatch A");
+  P4EST_GLOBAL_INFOF ("Edge balance with %lld quadrants and crc 0x%08x\n",
+                      (long long) p4estE->global_num_quadrants, crcE);
+#endif
+
+  /* test corner balance */
+  p4estC = p4est_copy (p4est, 1);
+#ifndef P4_TO_P8
+  p4est_balance (p4estF, P4EST_CONNECT_CORNER, NULL);
+  p4est_balance (p4estC, P4EST_CONNECT_CORNER, NULL);
+#else
+  p4est_balance (p4estF, P8EST_CONNECT_CORNER, NULL);
+  p4est_balance (p4estC, P8EST_CONNECT_CORNER, NULL);
+#endif
+  crcC = p4est_checksum (p4estC);
+  SC_CHECK_ABORT (crcC == p4est_checksum (p4estF), "mismatch B");
+  P4EST_GLOBAL_INFOF ("Corner balance with %lld quadrants and crc 0x%08x\n",
+                      (long long) p4estC->global_num_quadrants, crcC);
+
+  /* destroy forests and connectivity */
+  p4est_destroy (p4est);
+  p4est_destroy (p4estF);
+#ifdef P4_TO_P8
+  p4est_destroy (p4estE);
+#endif
+  p4est_destroy (p4estC);
+  p4est_connectivity_destroy (connectivity);
+
+  /* clean up and exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_balance_type3.c b/test/test_balance_type3.c
new file mode 100644
index 0000000..56a4bd7
--- /dev/null
+++ b/test/test_balance_type3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_balance_type2.c"
diff --git a/test/test_brick2.c b/test/test_brick2.c
new file mode 100644
index 0000000..3b0fdc8
--- /dev/null
+++ b/test/test_brick2.c
@@ -0,0 +1,493 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est.h>
+#else
+#include <p8est.h>
+#endif
+
+static inline       p4est_locidx_t
+qidx (p4est_locidx_t m, p4est_locidx_t n,
+      p4est_locidx_t i, p4est_locidx_t j, p4est_locidx_t k)
+{
+#ifndef P4_TO_P8
+  return m * j + i;
+#else
+  return m * n * k + m * j + i;
+#endif
+}
+
+static void
+#ifndef P4_TO_P8
+check_brick (p4est_connectivity_t * conn, int mi, int ni,
+             int periodic_a, int periodic_b)
+#else
+check_brick (p8est_connectivity_t * conn, int mi, int ni, int pi,
+             int periodic_a, int periodic_b, int periodic_c)
+#endif
+{
+  p4est_topidx_t      m = (p4est_topidx_t) mi;
+  p4est_topidx_t      n = (p4est_topidx_t) ni;
+  int                 i;
+  p4est_topidx_t      ti, tj, tk = 0;
+  p4est_topidx_t     *tree_to_vertex = conn->tree_to_vertex;
+  p4est_topidx_t     *tree_to_corner = conn->tree_to_corner;
+  p4est_topidx_t     *tree_to_tree = conn->tree_to_tree;
+  int8_t             *tree_to_face = conn->tree_to_face;
+  p4est_topidx_t     *ctt_offset = conn->ctt_offset;
+  p4est_topidx_t     *corner_to_tree = conn->corner_to_tree;
+  int8_t             *corner_to_corner = conn->corner_to_corner;
+  p4est_topidx_t      num_trees = conn->num_trees;
+  p4est_topidx_t      num_vertices = conn->num_vertices;
+  p4est_topidx_t      num_corners = conn->num_corners;
+  double             *vertices = conn->vertices;
+  double             *vertex[P4EST_CHILDREN];
+  int8_t             *vert_counter, *corn_counter;
+  p4est_topidx_t     *quad_counter;
+  int8_t              total, face1, face2, face3, corn1;
+  p4est_topidx_t      tx, ty, ttree1, ttree2, ttree3, tcorn1;
+  p4est_topidx_t      tz = 0;
+  p4est_topidx_t      diffx, diffy;
+#ifdef P4_TO_P8
+  p4est_topidx_t      p = (p4est_topidx_t) pi;
+  p4est_topidx_t     *tree_to_edge = conn->tree_to_edge;
+  p4est_topidx_t     *ett_offset = conn->ett_offset;
+  p4est_topidx_t     *edge_to_tree = conn->edge_to_tree;
+  int8_t             *edge_to_edge = conn->edge_to_edge;
+  p4est_topidx_t      num_edges = conn->num_edges;
+  int8_t             *edge_counter;
+  int8_t              edge1, edge2;
+  p4est_topidx_t      tedge1, tedge2;
+  p4est_topidx_t      diffz;
+#endif
+
+  SC_CHECK_ABORT (num_trees > 0, "no trees");
+#ifndef P4_TO_P8
+  SC_CHECK_ABORT (num_trees == m * n, "bad dimensions");
+  SC_CHECK_ABORT (num_vertices == (m + 1) * (n + 1),
+                  "wrong number of vertices");
+#else
+  SC_CHECK_ABORT (num_trees == m * n * p, "bad dimensions");
+  SC_CHECK_ABORT (num_vertices == (m + 1) * (n + 1) * (p + 1),
+                  "wrong number of vertices");
+#endif
+
+  quad_counter = P4EST_ALLOC (p4est_topidx_t, num_trees);
+  memset (quad_counter, -1, num_trees * sizeof (p4est_topidx_t));
+  vert_counter = P4EST_ALLOC_ZERO (int8_t, num_vertices);
+  corn_counter = NULL;
+  if (num_corners > 0) {
+    corn_counter = P4EST_ALLOC_ZERO (int8_t, num_corners);
+  }
+#ifdef P4_TO_P8
+  edge_counter = NULL;
+  if (num_edges > 0) {
+    edge_counter = P4EST_ALLOC_ZERO (int8_t, num_edges);
+  }
+#endif
+
+  for (ti = 0; ti < num_trees; ti++) {
+    for (i = 0; i < P4EST_CHILDREN; i++) {
+      vertex[i] = vertices + 3 * tree_to_vertex[ti * P4EST_CHILDREN + i];
+      vert_counter[tree_to_vertex[ti * P4EST_CHILDREN + i]]++;
+      if (num_corners > 0 && tree_to_corner[ti * P4EST_CHILDREN + i] != -1) {
+        corn_counter[tree_to_corner[ti * P4EST_CHILDREN + i]]++;
+      }
+    }
+    tx = (p4est_topidx_t) vertex[0][0];
+    ty = (p4est_topidx_t) vertex[0][1];
+#ifdef P4_TO_P8
+    tz = (p4est_topidx_t) vertex[0][2];
+#endif
+    SC_CHECK_ABORT (tx < m, "vertex coordinates out of range");
+    SC_CHECK_ABORT (ty < n, "vertex coordinates out of range");
+#ifdef P4_TO_P8
+    SC_CHECK_ABORT (tz < p, "vertex coordinates out of range");
+#endif
+    quad_counter[qidx (m, n, tx, ty, tz)] = ti;
+    for (i = 1; i < P4EST_CHILDREN; i++) {
+      tx = (p4est_locidx_t) (vertex[i][0] - vertex[0][0]);
+      ty = (p4est_locidx_t) (vertex[i][1] - vertex[0][1]);
+#ifdef P4_TO_P8
+      tz = (p4est_locidx_t) (vertex[i][2] - vertex[0][2]);
+#endif
+      if ((i & 1) == 1) {
+        SC_CHECK_ABORT (tx == 1, "non-unit vertex difference");
+      }
+      else {
+        SC_CHECK_ABORT (tx == 0, "non-unit vertex difference");
+      }
+      if (((i >> 1) & 1) == 1) {
+        SC_CHECK_ABORT (ty == 1, "non-unit vertex difference");
+      }
+      else {
+        SC_CHECK_ABORT (ty == 0, "non-unit vertex difference");
+      }
+#ifdef P4_TO_P8
+      if ((i >> 2) == 1) {
+        SC_CHECK_ABORT (tz == 1, "non-unit vertex difference");
+      }
+      else {
+        SC_CHECK_ABORT (tz == 0, "non-unit vertex difference");
+      }
+#endif
+    }
+#ifdef P4_TO_P8
+    if (num_edges > 0) {
+      for (i = 0; i < P8EST_EDGES; i++) {
+        if (tree_to_edge[ti * P8EST_EDGES + i] != -1) {
+          edge_counter[tree_to_edge[ti * P8EST_EDGES + i]]++;
+        }
+      }
+    }
+#endif
+  }
+
+  for (ti = 0; ti < m; ti++) {
+    for (tj = 0; tj < n; tj++) {
+#ifdef P4_TO_P8
+      for (tk = 0; tk < p; tk++) {
+#endif
+        SC_CHECK_ABORT (quad_counter[qidx (m, n, ti, tj, tk)] != -1,
+                        "grid points has no tree");
+#ifdef P4_TO_P8
+      }
+#endif
+    }
+  }
+
+  for (ti = 0; ti < num_vertices; ti++) {
+    tx = (p4est_topidx_t) vertices[ti * 3];
+    ty = (p4est_topidx_t) vertices[ti * 3 + 1];
+#ifdef P4_TO_P8
+    tz = (p4est_topidx_t) vertices[ti * 3 + 2];
+#endif
+    total = P4EST_CHILDREN;
+    if (tx == m || tx == 0) {
+      total /= 2;
+    }
+    if (ty == n || ty == 0) {
+      total /= 2;
+    }
+#ifdef P4_TO_P8
+    if (tz == p || tz == 0) {
+      total /= 2;
+    }
+#endif
+    SC_CHECK_ABORT (vert_counter[ti] == total,
+                    "vertex has too many or too few trees");
+  }
+
+  if (num_corners > 0) {
+    for (ti = 0; ti < num_corners; ti++) {
+      SC_CHECK_ABORT (corn_counter[ti] == P4EST_CHILDREN,
+                      "corner has too many or too few trees");
+      SC_CHECK_ABORT (ctt_offset[ti] == P4EST_CHILDREN * ti,
+                      "corner offset incorrect");
+    }
+    SC_CHECK_ABORT (ctt_offset[ti] == P4EST_CHILDREN * ti,
+                    "corner offset incorrect");
+  }
+
+#ifdef P4_TO_P8
+  if (num_edges > 0) {
+    for (ti = 0; ti < num_edges; ti++) {
+      SC_CHECK_ABORT (edge_counter[ti] == 4,
+                      "edge has too many or too few trees");
+      SC_CHECK_ABORT (ett_offset[ti] == 4 * ti, "edge offset incorrect");
+    }
+    SC_CHECK_ABORT (ett_offset[ti] == 4 * ti, "edge offset incorrect");
+  }
+#endif
+
+  for (ti = 0; ti < m; ti++) {
+    for (tj = 0; tj < n; tj++) {
+#ifdef P4_TO_P8
+      for (tk = 0; tk < p; tk++) {
+#endif
+        ttree1 = quad_counter[qidx (m, n, ti, tj, tk)];
+        for (face1 = 0; face1 < P4EST_FACES; face1++) {
+          ttree2 = tree_to_tree[ttree1 * P4EST_FACES + face1];
+          face2 = tree_to_face[ttree1 * P4EST_FACES + face1];
+          if (!periodic_a &&
+              ((face1 == 0 && ti == 0) || (face1 == 1 && ti == m - 1))) {
+            SC_CHECK_ABORT (ttree2 == ttree1 && face2 == face1,
+                            "boundary tree without boundary face");
+          }
+          else if (!periodic_b &&
+                   ((face1 == 2 && tj == 0) || (face1 == 3 && tj == n - 1))) {
+            SC_CHECK_ABORT (ttree2 == ttree1 && face2 == face1,
+                            "boundary tree without boundary face");
+          }
+#ifdef P4_TO_P8
+          else if (!periodic_c &&
+                   ((face1 == 4 && tk == 0) || (face1 == 5 && tk == p - 1))) {
+            SC_CHECK_ABORT (ttree2 == ttree1 && face2 == face1,
+                            "boundary tree without boundary face");
+          }
+#endif
+          else {
+            switch (face1) {
+            case 0:
+              ttree3 = quad_counter[qidx (m, n, (ti + m - 1) % m, tj, tk)];
+              break;
+            case 1:
+              ttree3 = quad_counter[qidx (m, n, (ti + 1) % m, tj, tk)];
+              break;
+            case 2:
+              ttree3 = quad_counter[qidx (m, n, ti, (tj + n - 1) % n, tk)];
+              break;
+            case 3:
+              ttree3 = quad_counter[qidx (m, n, ti, (tj + 1) % n, tk)];
+              break;
+#ifdef P4_TO_P8
+            case 4:
+              ttree3 = quad_counter[qidx (m, n, ti, tj, (tk + p - 1) % p)];
+              break;
+            case 5:
+              ttree3 = quad_counter[qidx (m, n, ti, tj, (tk + 1) % p)];
+              break;
+#endif
+            default:
+              SC_ABORT_NOT_REACHED ();
+            }
+            face3 = face1 ^ 1;
+            SC_CHECK_ABORT (ttree3 == ttree2 && face2 == face3,
+                            "tree has incorrect neighbor");
+            ttree3 = tree_to_tree[ttree2 * P4EST_FACES + face2];
+            SC_CHECK_ABORT (ttree1 == ttree3, "tree mismatch");
+            face3 = tree_to_face[ttree2 * P4EST_FACES + face2];
+            SC_CHECK_ABORT (face1 == face3, "face mismatch");
+          }
+        }
+#ifdef P4_TO_P8
+        if (num_edges > 0) {
+          for (edge1 = 0; edge1 < P8EST_EDGES; edge1++) {
+            if ((!periodic_b &&
+                 (((edge1 == 0 || edge1 == 2) && (tj == 0)) ||
+                  ((edge1 == 1 || edge1 == 3) && (tj == n - 1)))) ||
+                (!periodic_c &&
+                 (((edge1 == 0 || edge1 == 1) && (tk == 0)) ||
+                  ((edge1 == 2 || edge1 == 3) && (tk == p - 1))))) {
+              SC_CHECK_ABORT (tree_to_edge[ttree1 * P8EST_EDGES + edge1] ==
+                              -1, "boundary tree without boundary edge");
+            }
+            else if ((!periodic_a &&
+                      (((edge1 == 4 || edge1 == 6) && (ti == 0)) ||
+                       ((edge1 == 5 || edge1 == 7) && (ti == m - 1)))) ||
+                     (!periodic_c &&
+                      (((edge1 == 4 || edge1 == 5) && (tk == 0)) ||
+                       ((edge1 == 6 || edge1 == 7) && (tk == p - 1))))) {
+              SC_CHECK_ABORT (tree_to_edge[ttree1 * P8EST_EDGES + edge1] ==
+                              -1, "boundary tree without boundary edge");
+            }
+            else if ((!periodic_a &&
+                      (((edge1 == 8 || edge1 == 10) && (ti == 0)) ||
+                       ((edge1 == 9 || edge1 == 11) && (ti == m - 1)))) ||
+                     (!periodic_b &&
+                      (((edge1 == 8 || edge1 == 9) && (tj == 0)) ||
+                       ((edge1 == 10 || edge1 == 11) && (tj == n - 1))))) {
+              SC_CHECK_ABORT (tree_to_edge[ttree1 * P8EST_EDGES + edge1] ==
+                              -1, "boundary tree without boundary edge");
+            }
+            else {
+              tedge1 = tree_to_edge[ttree1 * P8EST_EDGES + edge1];
+              SC_CHECK_ABORT (edge_to_tree[4 * tedge1 + (3 - (edge1 % 4))] ==
+                              ttree1, "edge_to_tree mismatch");
+              SC_CHECK_ABORT (edge_to_edge[4 * tedge1 + (3 - (edge1 % 4))] ==
+                              edge1, "edge_to_edge mismatch");
+              ttree2 = tree_to_tree[ttree1 * 6 + p8est_edge_faces[edge1][0]];
+              edge2 = edge1 ^ 1;
+              tedge2 = tree_to_edge[ttree2 * P8EST_EDGES + edge2];
+              SC_CHECK_ABORT (tedge1 == tedge2,
+                              "face neighbor trees do not share edge");
+              SC_CHECK_ABORT (edge_to_tree[4 * tedge1 + (3 - (edge2 % 4))] ==
+                              ttree2,
+                              "edge does not recognize face neighbors");
+              SC_CHECK_ABORT (edge_to_edge[4 * tedge1 + (3 - (edge2 % 4))] ==
+                              edge2,
+                              "edge does not recognize face neighbors' edges");
+              ttree2 = tree_to_tree[ttree1 * 6 + p8est_edge_faces[edge1][1]];
+              edge2 = edge1 ^ 2;
+              tedge2 = tree_to_edge[ttree2 * P8EST_EDGES + edge2];
+              SC_CHECK_ABORT (tedge1 == tedge2,
+                              "face neighbor trees do not share edge");
+              SC_CHECK_ABORT (edge_to_tree[4 * tedge1 + (3 - (edge2 % 4))] ==
+                              ttree2,
+                              "edge does not recognize face neighbors");
+              SC_CHECK_ABORT (edge_to_edge[4 * tedge1 + (3 - (edge2 % 4))] ==
+                              edge2,
+                              "edge does not recognize face neighbors' edges");
+              ttree2 =
+                tree_to_tree[ttree2 * 6 + p8est_edge_faces[edge1 ^ 2][0]];
+              edge2 = edge1 ^ 3;
+              tedge2 = tree_to_edge[ttree2 * P8EST_EDGES + edge2];
+              SC_CHECK_ABORT (tedge1 == tedge2,
+                              "diagonal trees do not share edge");
+              SC_CHECK_ABORT (edge_to_tree[4 * tedge1 + (3 - (edge2 % 4))] ==
+                              ttree2,
+                              "edge does not recognize diagonal trees");
+              SC_CHECK_ABORT (edge_to_edge[4 * tedge1 + (3 - (edge2 % 4))] ==
+                              edge2,
+                              "edge does not recognize diagonal trees' edges");
+            }
+          }
+        }
+#endif
+        if (num_corners > 0) {
+          for (corn1 = 0; corn1 < P4EST_CHILDREN; corn1++) {
+            if ((!periodic_a &&
+                 (((corn1 & 1) == 0 && ti == 0) ||
+                  ((corn1 & 1) == 1 && ti == m - 1))) ||
+                (!periodic_b &&
+                 ((((corn1 >> 1) & 1) == 0 && tj == 0) ||
+                  (((corn1 >> 1) & 1) == 1 && tj == n - 1))) ||
+#ifdef P4_TO_P8
+                (!periodic_c &&
+                 (((corn1 >> 2) == 0 && tk == 0) ||
+                  ((corn1 >> 2) == 1 && tk == p - 1))) ||
+#endif
+                0) {
+              SC_CHECK_ABORT (tree_to_corner[ttree1 * P4EST_CHILDREN + corn1]
+                              == -1, "boundary tree without boundary corner");
+            }
+            else {
+              tcorn1 = tree_to_corner[ttree1 * P4EST_CHILDREN + corn1];
+              SC_CHECK_ABORT (corner_to_tree
+                              [tcorn1 * P4EST_CHILDREN +
+                               (P4EST_CHILDREN - 1 - corn1)] == ttree1,
+                              "corner_to_tree mismatch");
+              SC_CHECK_ABORT (corner_to_corner
+                              [tcorn1 * P4EST_CHILDREN + P4EST_CHILDREN - 1 -
+                               corn1] == corn1, "corner_to_corner mismatch");
+              for (i = 0; i < P4EST_CHILDREN; i++) {
+                ttree2 = corner_to_tree[tcorn1 * P4EST_CHILDREN + i];
+                tx =
+                  (p4est_topidx_t) vertices[3 *
+                                            tree_to_vertex[ttree2 *
+                                                           P4EST_CHILDREN]];
+                ty = (p4est_topidx_t)
+                  vertices[3 * tree_to_vertex[ttree2 * P4EST_CHILDREN] + 1];
+#ifdef P4_TO_P8
+                tz = (p4est_topidx_t)
+                  vertices[3 * tree_to_vertex[ttree2 * P4EST_CHILDREN] + 2];
+#endif
+                diffx = (i & 1) - ((P4EST_CHILDREN - 1 - corn1) & 1);
+                diffy =
+                  ((i >> 1) & 1) - (((P4EST_CHILDREN - 1 - corn1) >> 1) & 1);
+#ifdef P4_TO_P8
+                diffz = (i >> 2) - ((P4EST_CHILDREN - 1 - corn1) >> 2);
+#endif
+                SC_CHECK_ABORT ((ti + diffx + m) % m == tx,
+                                "unexpected trees around corner");
+                SC_CHECK_ABORT ((tj + diffy + n) % n == ty,
+                                "unexpected trees around corner");
+#ifdef P4_TO_P8
+                SC_CHECK_ABORT ((tk + diffz + p) % p == tz,
+                                "unexpected trees around corner");
+#endif
+              }
+            }
+          }
+        }
+#ifdef P4_TO_P8
+      }
+#endif
+    }
+  }
+
+#ifdef P4_TO_P8
+  if (num_edges > 0) {
+    P4EST_FREE (edge_counter);
+  }
+#endif
+  P4EST_FREE (vert_counter);
+  if (num_corners > 0) {
+    P4EST_FREE (corn_counter);
+  }
+  P4EST_FREE (quad_counter);
+
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 i, j;
+  int                 l, m;
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 size, rank;
+  p4est_connectivity_t *conn;
+#ifdef P4_TO_P8
+  int                 k, n;
+#endif
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &size);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  for (i = 1; i <= 5; i++) {
+    for (j = 1; j <= 5; j++) {
+#ifdef P4_TO_P8
+      for (k = 1; k <= 5; k++) {
+#endif
+        for (l = 0; l < 2; l++) {
+          for (m = 0; m < 2; m++) {
+#ifdef P4_TO_P8
+            for (n = 0; n < 2; n++) {
+#endif
+#ifndef P4_TO_P8
+              conn = p4est_connectivity_new_brick (i, j, l, m);
+              check_brick (conn, i, j, l, m);
+#else
+              conn = p4est_connectivity_new_brick (i, j, k, l, m, n);
+              check_brick (conn, i, j, k, l, m, n);
+#endif
+              p4est_connectivity_destroy (conn);
+#ifdef P4_TO_P8
+            }
+#endif
+          }
+        }
+#ifdef P4_TO_P8
+      }
+#endif
+    }
+  }
+
+  /* clean up and exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_brick3.c b/test/test_brick3.c
new file mode 100644
index 0000000..8cfb042
--- /dev/null
+++ b/test/test_brick3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_brick2.c"
diff --git a/test/test_coarsen2.c b/test/test_coarsen2.c
new file mode 100644
index 0000000..ab4b5f7
--- /dev/null
+++ b/test/test_coarsen2.c
@@ -0,0 +1,335 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_communication.h>
+#include <p4est_vtk.h>
+#else
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_communication.h>
+#include <p8est_vtk.h>
+#endif
+
+static void
+p4est_coarsen_old (p4est_t * p4est, int coarsen_recursive,
+                   p4est_coarsen_t coarsen_fn, p4est_init_t init_fn)
+{
+#ifdef P4EST_ENABLE_DEBUG
+  size_t              data_pool_size;
+#endif
+  int                 i, maxlevel;
+  int                 couldbegood;
+  size_t              zz;
+  size_t              incount, removed;
+  size_t              cidz, first, last, rest, before;
+  p4est_locidx_t      num_quadrants, prev_offset;
+  p4est_topidx_t      jt;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *c[P4EST_CHILDREN];
+  p4est_quadrant_t   *cfirst, *clast;
+  sc_array_t         *tquadrants;
+
+  P4EST_GLOBAL_PRODUCTIONF ("Into " P4EST_STRING
+                            "_coarsen_old with %lld total quadrants\n",
+                            (long long) p4est->global_num_quadrants);
+  p4est_log_indent_push ();
+  P4EST_ASSERT (p4est_is_valid (p4est));
+
+  /* loop over all local trees */
+  prev_offset = 0;
+  for (jt = p4est->first_local_tree; jt <= p4est->last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (p4est->trees, jt);
+    tquadrants = &tree->quadrants;
+#ifdef P4EST_ENABLE_DEBUG
+    data_pool_size = 0;
+    if (p4est->user_data_pool != NULL) {
+      data_pool_size = p4est->user_data_pool->elem_count;
+    }
+#endif
+    removed = 0;
+
+    /* initial log message for this tree */
+    P4EST_VERBOSEF ("Into coarsen tree %lld with %llu\n", (long long) jt,
+                    (unsigned long long) tquadrants->elem_count);
+
+    /* Initialize array indices.
+       If children are coarsened, the array will have an empty window.
+       first   index of the first child to be considered
+       last    index of the last child before the hole in the array
+       before  number of children before the hole in the array
+       rest    index of the first child after the hole in the array
+     */
+    first = last = 0;
+    before = rest = 1;
+
+    /* run through the array and coarsen recursively */
+    incount = tquadrants->elem_count;
+    while (rest + P4EST_CHILDREN - 1 - before < incount) {
+      couldbegood = 1;
+      for (zz = 0; zz < P4EST_CHILDREN; ++zz) {
+        if (zz < before) {
+          c[zz] = p4est_quadrant_array_index (tquadrants, first + zz);
+          if (zz != (size_t) p4est_quadrant_child_id (c[zz])) {
+            couldbegood = 0;
+            break;
+          }
+        }
+        else {
+          c[zz] = p4est_quadrant_array_index (tquadrants, rest + zz - before);
+        }
+      }
+      if (couldbegood && p4est_quadrant_is_familypv (c) &&
+          coarsen_fn (p4est, jt, c)) {
+        /* coarsen now */
+        for (zz = 0; zz < P4EST_CHILDREN; ++zz) {
+          p4est_quadrant_free_data (p4est, c[zz]);
+        }
+        tree->quadrants_per_level[c[0]->level] -= P4EST_CHILDREN;
+        cfirst = c[0];
+        p4est_quadrant_parent (c[0], cfirst);
+        p4est_quadrant_init_data (p4est, jt, cfirst, init_fn);
+        tree->quadrants_per_level[cfirst->level] += 1;
+        p4est->local_num_quadrants -= P4EST_CHILDREN - 1;
+        removed += P4EST_CHILDREN - 1;
+
+        rest += P4EST_CHILDREN - before;
+        if (coarsen_recursive) {
+          last = first;
+          cidz = (size_t) p4est_quadrant_child_id (cfirst);
+          if (cidz > first)
+            first = 0;
+          else
+            first -= cidz;
+        }
+        else {
+          /* don't coarsen again, move the counters and the hole */
+          P4EST_ASSERT (first == last && before == 1);
+          if (rest < incount) {
+            ++first;
+            cfirst = p4est_quadrant_array_index (tquadrants, first);
+            clast = p4est_quadrant_array_index (tquadrants, rest);
+            *cfirst = *clast;
+            last = first;
+            ++rest;
+          }
+        }
+      }
+      else {
+        /* do nothing, just move the counters and the hole */
+        ++first;
+        if (first > last) {
+          if (first != rest) {
+            cfirst = p4est_quadrant_array_index (tquadrants, first);
+            clast = p4est_quadrant_array_index (tquadrants, rest);
+            *cfirst = *clast;
+          }
+          last = first;
+          ++rest;
+        }
+      }
+      before = last - first + 1;
+    }
+
+    /* adjust final array size */
+    first = last;
+    if (first + 1 < rest) {
+      while (rest < incount) {
+        ++first;
+        cfirst = p4est_quadrant_array_index (tquadrants, first);
+        clast = p4est_quadrant_array_index (tquadrants, rest);
+        *cfirst = *clast;
+        ++rest;
+      }
+      sc_array_resize (tquadrants, first + 1);
+    }
+
+    /* compute maximum level */
+    maxlevel = 0;
+    num_quadrants = 0;
+    for (i = 0; i <= P4EST_QMAXLEVEL; ++i) {
+      P4EST_ASSERT (tree->quadrants_per_level[i] >= 0);
+      num_quadrants += tree->quadrants_per_level[i];    /* same type */
+      if (tree->quadrants_per_level[i] > 0) {
+        maxlevel = i;
+      }
+    }
+    tree->maxlevel = (int8_t) maxlevel;
+    tree->quadrants_offset = prev_offset;
+    prev_offset += num_quadrants;
+
+    /* do some sanity checks */
+    P4EST_ASSERT (num_quadrants == (p4est_locidx_t) tquadrants->elem_count);
+    P4EST_ASSERT (tquadrants->elem_count == incount - removed);
+    if (p4est->user_data_pool != NULL) {
+      P4EST_ASSERT (data_pool_size - removed ==
+                    p4est->user_data_pool->elem_count);
+    }
+    P4EST_ASSERT (p4est_tree_is_sorted (tree));
+    P4EST_ASSERT (p4est_tree_is_complete (tree));
+
+    /* final log message for this tree */
+    P4EST_VERBOSEF ("Done coarsen tree %lld now %llu\n", (long long) jt,
+                    (unsigned long long) tquadrants->elem_count);
+  }
+  if (p4est->last_local_tree >= 0) {
+    for (; jt < p4est->connectivity->num_trees; ++jt) {
+      tree = p4est_tree_array_index (p4est->trees, jt);
+      tree->quadrants_offset = p4est->local_num_quadrants;
+    }
+  }
+
+  /* compute global number of quadrants */
+  p4est_comm_count_quadrants (p4est);
+
+  P4EST_ASSERT (p4est_is_valid (p4est));
+  p4est_log_indent_pop ();
+  P4EST_GLOBAL_PRODUCTIONF ("Done " P4EST_STRING
+                            "_coarsen_old with %lld total quadrants\n",
+                            (long long) p4est->global_num_quadrants);
+}
+
+#ifndef P4_TO_P8
+static const int    refine_level = 6;
+#else
+static const int    refine_level = 4;
+#endif
+static int          refine_callback_count;
+static int          coarsen_all = 1;
+static int          coarsen_callback_count;
+
+static int
+test_refine (p4est_t * p4est, p4est_topidx_t which_tree,
+             p4est_quadrant_t * quadrant)
+{
+  ++refine_callback_count;
+
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int
+test_coarsen (p4est_t * p4est, p4est_topidx_t which_tree,
+              p4est_quadrant_t * q[])
+{
+  ++coarsen_callback_count;
+  if (q[1] == NULL) {
+    return 0;
+  }
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (q), "Coarsen invocation");
+
+  return coarsen_all || q[0]->y >= P4EST_ROOT_LEN / 2;
+}
+
+static void
+p4est_coarsen_both (p4est_t * p4est, int coarsen_recursive,
+                    p4est_coarsen_t coarsen_fn, p4est_init_t init_fn)
+{
+  int                 success;
+  p4est_t            *copy;
+
+  copy = p4est_copy (p4est, 1);
+  p4est_coarsen_old (copy, coarsen_recursive, coarsen_fn, init_fn);
+
+  coarsen_callback_count = 0;
+  p4est_coarsen_ext (p4est, coarsen_recursive, 1, coarsen_fn, init_fn, NULL);
+  SC_CHECK_ABORT (coarsen_recursive ||
+                  coarsen_callback_count == (int) p4est->local_num_quadrants,
+                  "Coarsen count");
+
+  success = p4est_is_equal (p4est, copy, 1);
+  SC_CHECK_ABORT (success, "Coarsen mismatch");
+
+  p4est_destroy (copy);
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  sc_MPI_Comm         mpicomm;
+  p4est_t            *p4est;
+  p4est_connectivity_t *connectivity;
+  p4est_locidx_t      save_local_count;
+  p4est_geometry_t   *geom;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* create connectivity and forest structures */
+#ifdef P4_TO_P8
+  connectivity = p8est_connectivity_new_rotcubes ();
+  geom = NULL;
+#else
+  connectivity = p4est_connectivity_new_star ();
+  geom = p4est_geometry_new_connectivity (connectivity);
+#endif
+  p4est = p4est_new_ext (mpicomm, connectivity, 15, 0, 0, 0, NULL, NULL);
+
+  save_local_count = p4est->local_num_quadrants;
+  refine_callback_count = 0;
+  p4est_refine_ext (p4est, 0, 2, test_refine, NULL, NULL);
+  SC_CHECK_ABORT (refine_callback_count == save_local_count, "Refine count");
+
+  refine_callback_count = 0;
+  p4est_refine (p4est, 1, test_refine, NULL);
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+
+  coarsen_all = 1;
+  p4est_coarsen_both (p4est, 0, test_coarsen, NULL);
+  coarsen_all = 0;
+  p4est_coarsen_both (p4est, 1, test_coarsen, NULL);
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+  coarsen_all = 1;
+
+  p4est_coarsen_both (p4est, 1, test_coarsen, NULL);
+  p4est_vtk_write_file (p4est, geom, P4EST_STRING "_endcoarsen");
+
+  if (p4est->mpisize == 1) {
+    SC_CHECK_ABORT (p4est->global_num_quadrants ==
+                    (p4est_gloidx_t) connectivity->num_trees, "Coarsen all");
+  }
+
+  p4est_destroy (p4est);
+  if (geom != NULL) {
+    p4est_geometry_destroy (geom);
+  }
+  p4est_connectivity_destroy (connectivity);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_coarsen3.c b/test/test_coarsen3.c
new file mode 100644
index 0000000..98fef9f
--- /dev/null
+++ b/test/test_coarsen3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_coarsen2.c"
diff --git a/test/test_comm.c b/test/test_comm.c
new file mode 100644
index 0000000..bcbf2b0
--- /dev/null
+++ b/test/test_comm.c
@@ -0,0 +1,153 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_algorithms.h>
+#include <p4est_communication.h>
+#include <p4est_extended.h>
+
+typedef struct
+{
+  p4est_topidx_t      a;
+}
+user_data_t;
+
+static void
+init_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+         p4est_quadrant_t * quadrant)
+{
+  user_data_t        *data = (user_data_t *) quadrant->p.user_data;
+
+  data->a = which_tree;
+}
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  if (quadrant->level >= 6) {
+    return 0;
+  }
+  if (quadrant->x == P4EST_LAST_OFFSET (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->x >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static void
+test_pertree (p4est_t * p4est)
+{
+  p4est_topidx_t      num_trees;
+  p4est_gloidx_t     *pertree;
+
+  num_trees = p4est->connectivity->num_trees;
+  P4EST_ASSERT ((size_t) num_trees == p4est->trees->elem_count);
+  pertree = P4EST_ALLOC (p4est_gloidx_t, num_trees + 1);
+  p4est_comm_count_pertree (p4est, pertree);
+  SC_CHECK_ABORT (pertree[num_trees] == p4est->global_num_quadrants,
+                  "pertree check failed");
+  P4EST_FREE (pertree);
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 num_procs, rank;
+  int                 mpiret;
+  sc_MPI_Comm         mpicomm;
+  p4est_t            *p4est;
+  p4est_connectivity_t *connectivity;
+  p4est_gloidx_t      qglobal, qlocal, qbegin, qend, qsum;
+  int                 i;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+
+  /* create connectivity and forest structures */
+  connectivity = p4est_connectivity_new_corner ();
+  p4est = p4est_new_ext (mpicomm, connectivity, 15, 0, 0,
+                         sizeof (user_data_t), init_fn, NULL);
+
+  num_procs = p4est->mpisize;
+
+  /* test tree counting */
+  test_pertree (p4est);
+
+  /* refine and balance to make the number of elements interesting */
+  p4est_refine (p4est, 1, refine_fn, init_fn);
+
+  /* test tree counting */
+  test_pertree (p4est);
+
+  /* Check the global number of elements */
+  qlocal = p4est->local_num_quadrants;
+  qglobal = -13473829;
+  mpiret =
+    sc_MPI_Allreduce (&qlocal, &qglobal, 1, P4EST_MPI_GLOIDX, sc_MPI_SUM,
+                      p4est->mpicomm);
+  SC_CHECK_MPI (mpiret);
+  SC_CHECK_ABORT (qglobal == p4est->global_num_quadrants,
+                  "wrong number of p4est->global_num_quadrants");
+
+  /* Check the number of elements per proc */
+  qsum = 0;
+  for (i = 0; i < num_procs; ++i) {
+    if (i == rank) {
+      qlocal = p4est->local_num_quadrants;
+    }
+    else {
+      qlocal = 0;
+    }
+
+    qglobal = qlocal;
+    mpiret = sc_MPI_Bcast (&qglobal, 1, P4EST_MPI_GLOIDX, i, p4est->mpicomm);
+    SC_CHECK_MPI (mpiret);
+
+    qsum += qglobal;
+    qbegin = p4est->global_first_quadrant[i];
+    qend = p4est->global_first_quadrant[i + 1];
+    SC_CHECK_ABORT (qglobal == qend - qbegin,
+                    "wrong number in p4est->global_first_quadrant");
+  }
+  SC_CHECK_ABORT (qsum == p4est->global_num_quadrants,
+                  "Wrong number after quadrant counting");
+
+  /* clean up and exit */
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (connectivity);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_conn_complete2.c b/test/test_conn_complete2.c
new file mode 100644
index 0000000..0a961be
--- /dev/null
+++ b/test/test_conn_complete2.c
@@ -0,0 +1,110 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_connectivity.h>
+#include <p4est_ghost.h>
+#include <p4est_lnodes.h>
+#else
+#include <p8est_connectivity.h>
+#include <p8est_ghost.h>
+#include <p8est_lnodes.h>
+#endif
+
+static void
+test_the_p4est (p4est_connectivity_t * conn, int N)
+{
+  p4est_t            *p4est;
+  p4est_ghost_t      *ghost;
+  p4est_lnodes_t     *lnodes;
+
+  p4est = p4est_new (sc_MPI_COMM_WORLD, conn, 0, NULL, NULL);
+  ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+  lnodes = p4est_lnodes_new (p4est, ghost, N);
+  p4est_lnodes_destroy (lnodes);
+  p4est_ghost_destroy (ghost);
+  p4est_destroy (p4est);
+}
+
+static void
+test_complete (p4est_connectivity_t * conn, const char *which, int test_p4est)
+{
+  SC_GLOBAL_INFOF ("Testing standard connectivity %s\n", which);
+  SC_CHECK_ABORTF (p4est_connectivity_is_valid (conn),
+                   "Invalid connectivity %s before completion", which);
+  if (0 && test_p4est) {
+    test_the_p4est (conn, 3);
+  }
+
+  SC_GLOBAL_INFOF ("Testing completion for connectivity %s\n", which);
+  p4est_connectivity_complete (conn);
+  SC_CHECK_ABORTF (p4est_connectivity_is_valid (conn),
+                   "Invalid connectivity %s after completion", which);
+  if (test_p4est) {
+    test_the_p4est (conn, 3);
+  }
+
+  p4est_connectivity_destroy (conn);
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+#ifndef P4_TO_P8
+  test_complete (p4est_connectivity_new_unitsquare (), "unitsquare", 1);
+  test_complete (p4est_connectivity_new_periodic (), "2D periodic", 0);
+  test_complete (p4est_connectivity_new_rotwrap (), "rotwrap", 0);
+  test_complete (p4est_connectivity_new_corner (), "corner", 1);
+  test_complete (p4est_connectivity_new_moebius (), "moebius", 1);
+  test_complete (p4est_connectivity_new_star (), "star", 1);
+  test_complete (p4est_connectivity_new_brick (3, 18, 0, 1),
+                 "2D periodic brick", 0);
+  test_complete (p4est_connectivity_new_brick (3, 18, 0, 0), "2D brick", 1);
+#else
+  test_complete (p8est_connectivity_new_unitcube (), "unitcube", 1);
+  test_complete (p8est_connectivity_new_periodic (), "3D periodic", 0);
+  test_complete (p8est_connectivity_new_rotwrap (), "rotwrap", 0);
+  test_complete (p8est_connectivity_new_twowrap (), "twowrap", 1);
+  test_complete (p8est_connectivity_new_twocubes (), "twocubes", 1);
+  test_complete (p8est_connectivity_new_rotcubes (), "rotcubes", 1);
+  test_complete (p8est_connectivity_new_brick (3, 2, 8, 1, 0, 1),
+                 "3D periodic brick", 0);
+  test_complete (p8est_connectivity_new_brick (3, 2, 8, 0, 0, 0),
+                 "3D brick", 1);
+#endif
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_conn_complete3.c b/test/test_conn_complete3.c
new file mode 100644
index 0000000..46ca1eb
--- /dev/null
+++ b/test/test_conn_complete3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_conn_complete2.c"
diff --git a/test/test_conn_reduce2.c b/test/test_conn_reduce2.c
new file mode 100644
index 0000000..0ecb3c7
--- /dev/null
+++ b/test/test_conn_reduce2.c
@@ -0,0 +1,92 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_connectivity.h>
+#else
+#include <p8est_connectivity.h>
+#endif
+
+static void
+test_reduce (p4est_connectivity_t * conn, const char *which)
+{
+  SC_GLOBAL_INFOF ("Testing standard connectivity %s\n", which);
+  SC_CHECK_ABORTF (p4est_connectivity_is_valid (conn),
+                   "Invalid connectivity %s before reduce", which);
+  p4est_connectivity_reduce (conn);
+  SC_CHECK_ABORTF (p4est_connectivity_is_valid (conn),
+                   "Invalid connectivity %s after reduce", which);
+  SC_GLOBAL_INFOF ("Testing completion for reduced connectivity %s\n", which);
+  p4est_connectivity_complete (conn);
+  SC_CHECK_ABORTF (p4est_connectivity_is_valid (conn),
+                   "Invalid connectivity %s after completion", which);
+  p4est_connectivity_destroy (conn);
+}
+
+int
+main (int argc, char *argv[])
+{
+  int                 mpiret;
+
+  /* initialize MPI and p4est internals */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+#ifndef P4_TO_P8
+  test_reduce (p4est_connectivity_new_unitsquare (), "unitsquare");
+  test_reduce (p4est_connectivity_new_periodic (), "periodic");
+  test_reduce (p4est_connectivity_new_rotwrap (), "rotwrap");
+  test_reduce (p4est_connectivity_new_corner (), "corner");
+  test_reduce (p4est_connectivity_new_pillow (), "pillow");
+  test_reduce (p4est_connectivity_new_moebius (), "moebius");
+  test_reduce (p4est_connectivity_new_star (), "star");
+  test_reduce (p4est_connectivity_new_cubed (), "cubed");
+  test_reduce (p4est_connectivity_new_disk (), "disk");
+  test_reduce (p4est_connectivity_new_brick (3, 2, 0, 0), "brick00");
+  test_reduce (p4est_connectivity_new_brick (3, 2, 0, 1), "brick01");
+  test_reduce (p4est_connectivity_new_brick (3, 2, 1, 0), "brick10");
+  test_reduce (p4est_connectivity_new_brick (3, 2, 1, 1), "brick11");
+#else
+  test_reduce (p8est_connectivity_new_unitcube (), "unitcube");
+  test_reduce (p8est_connectivity_new_periodic (), "periodic");
+  test_reduce (p8est_connectivity_new_rotwrap (), "rotwrap");
+  test_reduce (p8est_connectivity_new_twocubes (), "twocubes");
+  test_reduce (p8est_connectivity_new_twowrap (), "twowrap");
+  test_reduce (p8est_connectivity_new_rotcubes (), "rotcubes");
+  test_reduce (p8est_connectivity_new_brick (4, 3, 2, 0, 0, 0), "brick000");
+  test_reduce (p8est_connectivity_new_brick (4, 3, 2, 0, 0, 1), "brick001");
+  test_reduce (p8est_connectivity_new_brick (4, 3, 2, 0, 1, 0), "brick010");
+  test_reduce (p8est_connectivity_new_brick (4, 3, 2, 0, 1, 1), "brick011");
+  test_reduce (p8est_connectivity_new_brick (4, 3, 2, 1, 0, 0), "brick100");
+  test_reduce (p8est_connectivity_new_brick (4, 3, 2, 1, 0, 1), "brick101");
+  test_reduce (p8est_connectivity_new_brick (4, 3, 2, 1, 1, 1), "brick111");
+#endif
+  /* clean up and exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+  return 0;
+}
diff --git a/test/test_conn_reduce3.c b/test/test_conn_reduce3.c
new file mode 100644
index 0000000..7d2bff4
--- /dev/null
+++ b/test/test_conn_reduce3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_conn_reduce2.c"
diff --git a/test/test_edge_face_corners3.c b/test/test_edge_face_corners3.c
new file mode 100644
index 0000000..721b234
--- /dev/null
+++ b/test/test_edge_face_corners3.c
@@ -0,0 +1,95 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * Purpose of this program is to verify the edge face corner connections
+ * that are computed in the static function p8est_compute_edge_face_corners.
+ */
+
+#include <p8est.h>
+#include <p4est_to_p8est.h>
+
+static int
+p8est_compute_edge_face_corners (int edge, int face, int corners[])
+{
+  int                 nfound = 0;
+  int                 fc, c0, c1, cx;
+
+  P4EST_ASSERT (0 <= edge && edge < 12);
+  P4EST_ASSERT (0 <= face && face < 6);
+
+  c0 = p8est_edge_corners[edge][0];
+  c1 = p8est_edge_corners[edge][1];
+
+  for (fc = 0; fc < 4; ++fc) {
+    cx = p8est_face_corners[face][fc];
+    if (c0 == cx) {
+      corners[0] = fc;
+      ++nfound;
+      continue;
+    }
+    if (c1 == cx) {
+      corners[1] = fc;
+      ++nfound;
+      continue;
+    }
+  }
+  P4EST_ASSERT (nfound <= 2);
+
+  return nfound == 2;
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 edge, face, cs[2];
+  int                 success;
+
+  sc_init (sc_MPI_COMM_NULL, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  for (edge = 0; edge < 12; ++edge) {
+    for (face = 0; face < 6; ++face) {
+      cs[0] = cs[1] = 0;        /* to avoid compiler warning */
+      success = p8est_compute_edge_face_corners (edge, face, cs);
+
+      if (!success) {
+        P4EST_LDEBUGF ("Nothing for %d %d\n", edge, face);
+        SC_CHECK_ABORT (p8est_edge_face_corners[edge][face][0] == -1 &&
+                        p8est_edge_face_corners[edge][face][1] == -1,
+                        "Invalid nonexisting connection");
+      }
+      else {
+        P4EST_LDEBUGF ("Results for %d %d are %d %d\n",
+                       edge, face, cs[0], cs[1]);
+        SC_CHECK_ABORT (p8est_edge_face_corners[edge][face][0] == cs[0] &&
+                        p8est_edge_face_corners[edge][face][1] == cs[1],
+                        "Invalid existing connection");
+      }
+    }
+  }
+
+  sc_finalize ();
+
+  return 0;
+}
diff --git a/test/test_face_transform3.c b/test/test_face_transform3.c
new file mode 100644
index 0000000..3ceb228
--- /dev/null
+++ b/test/test_face_transform3.c
@@ -0,0 +1,152 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * Purpose of this program is to verify the face transformation codes
+ * that are computed in a fast but cryptic way in p8est_find_face_transform.
+ */
+
+#include <p8est.h>
+#include <p4est_to_p8est.h>
+
+int
+main (int argc, char **argv)
+{
+  int                 my_face, target_face, orientation;
+  int                 face_ref, face_perm;
+  int                 low[2], high[2], swap;
+  int                 i, reverse;
+  int                 ft[9], gt[9];
+  int                *my_axis = &ft[0];
+  int                *target_axis = &ft[3];
+  int                *edge_reverse = &ft[6];
+
+  sc_init (sc_MPI_COMM_NULL, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  for (my_face = 0; my_face < 2 * P4EST_DIM; ++my_face) {
+    for (target_face = 0; target_face < 2 * P4EST_DIM; ++target_face) {
+      for (orientation = 0; orientation < 4; ++orientation) {
+
+        /* find if my edges 0 and 2 are parallel to the x, y, or z-axis */
+        my_axis[0] = p8est_face_edges[my_face][0] / 4;
+        my_axis[1] = p8est_face_edges[my_face][2] / 4;
+        target_axis[0] = target_axis[1] = -1;
+        edge_reverse[0] = edge_reverse[1] = 0;
+
+        /* find matching target vertices */
+        face_ref = p8est_face_permutation_refs[my_face][target_face];
+        face_perm = p8est_face_permutation_sets[face_ref][orientation];
+        low[0] = low[1] =
+          p8est_face_corners[target_face][p8est_face_permutations[face_perm]
+                                          [0]];
+        high[0] =
+          p8est_face_corners[target_face][p8est_face_permutations[face_perm]
+                                          [1]];
+        high[1] =
+          p8est_face_corners[target_face][p8est_face_permutations[face_perm]
+                                          [2]];
+        if (low[0] > high[0]) {
+          swap = low[0];
+          low[0] = high[0];
+          high[0] = swap;
+          edge_reverse[0] = 1;
+        }
+        if (low[1] > high[1]) {
+          swap = low[1];
+          low[1] = high[1];
+          high[1] = swap;
+          edge_reverse[1] = 1;
+        }
+
+        /* find matching target edges */
+        for (i = 0; i < 12; ++i) {
+          if (low[0] == p8est_edge_corners[i][0] &&
+              high[0] == p8est_edge_corners[i][1]) {
+            P4EST_ASSERT (target_axis[0] == -1);
+            target_axis[0] = i / 4;
+#ifndef P4EST_ENABLE_DEBUG
+            if (target_axis[1] >= 0)
+              break;
+#endif
+          }
+          else if (low[1] == p8est_edge_corners[i][0] &&
+                   high[1] == p8est_edge_corners[i][1]) {
+            P4EST_ASSERT (target_axis[1] == -1);
+            target_axis[1] = i / 4;
+#ifndef P4EST_ENABLE_DEBUG
+            if (target_axis[0] >= 0)
+              break;
+#endif
+          }
+        }
+
+        /* find what axis is normal to the faces */
+        my_axis[2] = my_face / 2;
+        target_axis[2] = target_face / 2;
+        edge_reverse[2] = 2 * (my_face % 2) + target_face % 2;
+
+#ifdef P4EST_ENABLE_DEBUG
+        for (i = 0; i < 3; ++i) {
+          P4EST_ASSERT (0 <= my_axis[i] && my_axis[i] < 3);
+          P4EST_ASSERT (0 <= target_axis[i] && target_axis[i] < 3);
+        }
+        P4EST_ASSERT (my_axis[0] != my_axis[1] &&
+                      my_axis[0] != my_axis[2] && my_axis[1] != my_axis[2]);
+        P4EST_ASSERT (target_axis[0] != target_axis[1] &&
+                      target_axis[0] != target_axis[2] &&
+                      target_axis[1] != target_axis[2]);
+#endif
+
+        /* output the results */
+        P4EST_LDEBUGF
+          ("Results for %d %d %d are %d %d %d %d %d %d %d %d %d\n",
+           my_face, target_face, orientation, ft[0], ft[1], ft[2],
+           ft[3], ft[4], ft[5], ft[6], ft[7], ft[8]);
+
+        /* compute the transformation code in a faster way and compare */
+        gt[0] = my_face < 2 ? 1 : 0;
+        gt[1] = my_face < 4 ? 2 : 1;
+        gt[2] = my_face / 2;
+        reverse =
+          p8est_face_permutation_refs[0][my_face] ^
+          p8est_face_permutation_refs[0][target_face] ^
+          (orientation == 0 || orientation == 3);
+        gt[3 + reverse] = target_face < 2 ? 1 : 0;
+        gt[3 + !reverse] = target_face < 4 ? 2 : 1;
+        gt[5] = target_face / 2;
+        reverse = p8est_face_permutation_refs[my_face][target_face] == 1;
+        gt[6 + reverse] = orientation % 2;
+        gt[6 + !reverse] = orientation / 2;
+        gt[8] = 2 * (my_face % 2) + target_face % 2;
+
+        /* ensure that both computations yield the same result */
+        SC_CHECK_ABORT (!memcmp (ft, gt, 9 * sizeof (int)), "Mismatch");
+      }
+    }
+  }
+
+  sc_finalize ();
+
+  return 0;
+}
diff --git a/test/test_ghost2.c b/test/test_ghost2.c
new file mode 100644
index 0000000..14060af
--- /dev/null
+++ b/test/test_ghost2.c
@@ -0,0 +1,405 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_ghost.h>
+#include <p4est_lnodes.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_ghost.h>
+#include <p8est_lnodes.h>
+#endif
+
+#ifndef P4_TO_P8
+static int          refine_level = 5;
+#else
+static int          refine_level = 4;
+#endif
+
+/* #undef P4EST_TEST_CHATTY */
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  if (which_tree == 2 || which_tree == 3) {
+    return 0;
+  }
+
+  cid = p4est_quadrant_child_id (quadrant);
+
+  if (cid == P4EST_CHILDREN - 1 ||
+      (quadrant->x >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2) &&
+       quadrant->y >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#ifdef P4_TO_P8
+       && quadrant->z >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#endif
+      )) {
+    return 1;
+  }
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && cid == 2) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_QUADRANT_LEN (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->y >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+#define TEST_EXCHANGE_MAGIC 427482.18e-13
+
+typedef struct test_exchange
+{
+  p4est_gloidx_t      gi;
+  long long           ll;
+  double              magic;
+}
+test_exchange_t;
+
+static void
+test_exchange_A (p4est_t * p4est, p4est_ghost_t * ghost)
+{
+  int                 p;
+  size_t              zz;
+  p4est_topidx_t      nt;
+  p4est_locidx_t      gexcl, gincl, gl;
+  p4est_gloidx_t      gnum;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *q;
+  void              **ghost_void_data;
+
+  /* Test A: p4est data size is 0, transfer what's in the user_data void* */
+
+  p4est_reset_data (p4est, 0, NULL, NULL);
+  gnum = p4est->global_first_quadrant[p4est->mpirank];
+  for (nt = p4est->first_local_tree; nt <= p4est->last_local_tree; ++nt) {
+    tree = p4est_tree_array_index (p4est->trees, nt);
+    for (zz = 0; zz < tree->quadrants.elem_count; ++gnum, ++zz) {
+      q = p4est_quadrant_array_index (&tree->quadrants, zz);
+      q->p.user_long = (long) gnum;
+    }
+  }
+  P4EST_ASSERT (gnum == p4est->global_first_quadrant[p4est->mpirank + 1]);
+
+  ghost_void_data = P4EST_ALLOC (void *, ghost->ghosts.elem_count);
+  p4est_ghost_exchange_data (p4est, ghost, ghost_void_data);
+
+  gexcl = 0;
+  for (p = 0; p < p4est->mpisize; ++p) {
+    gincl = ghost->proc_offsets[p + 1];
+    gnum = p4est->global_first_quadrant[p];
+#ifdef P4EST_TEST_CHATTY
+    P4EST_LDEBUGF ("In test A for %d with %d %d\n", p, gexcl, gincl);
+#endif
+    for (gl = gexcl; gl < gincl; ++gl) {
+      q = p4est_quadrant_array_index (&ghost->ghosts, gl);
+      SC_CHECK_ABORT (gnum + (p4est_gloidx_t) q->p.piggy3.local_num ==
+                      (p4est_gloidx_t) ghost_void_data[gl],
+                      "Ghost exchange mismatch A");
+    }
+    gexcl = gincl;
+  }
+  P4EST_ASSERT (gexcl == (p4est_locidx_t) ghost->ghosts.elem_count);
+  P4EST_FREE (ghost_void_data);
+}
+
+static void
+test_exchange_B (p4est_t * p4est, p4est_ghost_t * ghost)
+{
+  int                 p;
+  size_t              zz;
+  p4est_topidx_t      nt;
+  p4est_locidx_t      gexcl, gincl, gl;
+  p4est_gloidx_t      gnum;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *q;
+  test_exchange_t    *ghost_struct_data, *e;
+
+  /* Test B: p4est data size is > 0, transfer what's in user_data */
+
+  p4est_reset_data (p4est, sizeof (test_exchange_t), NULL, NULL);
+  gnum = p4est->global_first_quadrant[p4est->mpirank];
+  for (nt = p4est->first_local_tree; nt <= p4est->last_local_tree; ++nt) {
+    tree = p4est_tree_array_index (p4est->trees, nt);
+    for (zz = 0; zz < tree->quadrants.elem_count; ++gnum, ++zz) {
+      q = p4est_quadrant_array_index (&tree->quadrants, zz);
+      e = (test_exchange_t *) q->p.user_data;
+      e->gi = gnum;
+      e->ll = (long) gnum;
+      e->magic = TEST_EXCHANGE_MAGIC;
+    }
+  }
+  P4EST_ASSERT (gnum == p4est->global_first_quadrant[p4est->mpirank + 1]);
+
+  ghost_struct_data = P4EST_ALLOC (test_exchange_t, ghost->ghosts.elem_count);
+  p4est_ghost_exchange_data (p4est, ghost, ghost_struct_data);
+
+  gexcl = 0;
+  for (p = 0; p < p4est->mpisize; ++p) {
+    gincl = ghost->proc_offsets[p + 1];
+    gnum = p4est->global_first_quadrant[p];
+#ifdef P4EST_TEST_CHATTY
+    P4EST_LDEBUGF ("In test B for %d with %d %d\n", p, gexcl, gincl);
+#endif
+    for (gl = gexcl; gl < gincl; ++gl) {
+      q = p4est_quadrant_array_index (&ghost->ghosts, gl);
+      e = ghost_struct_data + gl;
+      SC_CHECK_ABORT (gnum + (p4est_gloidx_t) q->p.piggy3.local_num ==
+                      e->gi, "Ghost exchange mismatch B1");
+      SC_CHECK_ABORT (gnum + (p4est_gloidx_t) q->p.piggy3.local_num ==
+                      (p4est_gloidx_t) e->ll, "Ghost exchange mismatch B2");
+      SC_CHECK_ABORT (e->magic == TEST_EXCHANGE_MAGIC,
+                      "Ghost exchange mismatch B3");
+    }
+    gexcl = gincl;
+  }
+  P4EST_ASSERT (gexcl == (p4est_locidx_t) ghost->ghosts.elem_count);
+  P4EST_FREE (ghost_struct_data);
+}
+
+static void
+test_exchange_C (p4est_t * p4est, p4est_ghost_t * ghost)
+{
+  int                 p;
+  size_t              zz;
+  p4est_locidx_t      gexcl, gincl, gl;
+  p4est_gloidx_t      gnum;
+  p4est_quadrant_t   *q;
+  void              **mirror_data;
+  test_exchange_t    *mirror_struct_data;
+  test_exchange_t    *ghost_struct_data, *e;
+
+  /* Test C: don't use p4est user_data at all */
+
+  mirror_struct_data =
+    P4EST_ALLOC (test_exchange_t, ghost->mirrors.elem_count);
+  mirror_data = P4EST_ALLOC (void *, ghost->mirrors.elem_count);
+  for (zz = 0; zz < ghost->mirrors.elem_count; ++zz) {
+    q = p4est_quadrant_array_index (&ghost->mirrors, zz);
+    gnum = p4est->global_first_quadrant[p4est->mpirank] +
+      (p4est_gloidx_t) q->p.piggy3.local_num;
+    mirror_data[zz] = e = mirror_struct_data + zz;
+    e->gi = gnum;
+    e->ll = (long) gnum;
+    e->magic = TEST_EXCHANGE_MAGIC;
+  }
+
+  ghost_struct_data = P4EST_ALLOC (test_exchange_t, ghost->ghosts.elem_count);
+  p4est_ghost_exchange_custom (p4est, ghost, sizeof (test_exchange_t),
+                               mirror_data, ghost_struct_data);
+
+  P4EST_FREE (mirror_data);
+  P4EST_FREE (mirror_struct_data);
+
+  gexcl = 0;
+  for (p = 0; p < p4est->mpisize; ++p) {
+    gincl = ghost->proc_offsets[p + 1];
+    gnum = p4est->global_first_quadrant[p];
+#ifdef P4EST_TEST_CHATTY
+    P4EST_LDEBUGF ("In test C for %d with %d %d\n", p, gexcl, gincl);
+#endif
+    for (gl = gexcl; gl < gincl; ++gl) {
+      q = p4est_quadrant_array_index (&ghost->ghosts, gl);
+      e = ghost_struct_data + gl;
+      SC_CHECK_ABORT (gnum + (p4est_gloidx_t) q->p.piggy3.local_num ==
+                      e->gi, "Ghost exchange mismatch C1");
+      SC_CHECK_ABORT (gnum + (p4est_gloidx_t) q->p.piggy3.local_num ==
+                      (p4est_gloidx_t) e->ll, "Ghost exchange mismatch C2");
+      SC_CHECK_ABORT (e->magic == TEST_EXCHANGE_MAGIC,
+                      "Ghost exchange mismatch C3");
+    }
+    gexcl = gincl;
+  }
+  P4EST_ASSERT (gexcl == (p4est_locidx_t) ghost->ghosts.elem_count);
+  P4EST_FREE (ghost_struct_data);
+}
+
+static void
+test_exchange_D (p4est_t * p4est, p4est_ghost_t * ghost)
+{
+  const int           exchange_minlevel = 1;
+  const int           exchange_maxlevel = refine_level - 1;
+  int                 p;
+  size_t              zz;
+  p4est_locidx_t      gexcl, gincl, gl;
+  p4est_gloidx_t      gnum;
+  p4est_quadrant_t   *q;
+  void              **mirror_data;
+  test_exchange_t    *mirror_struct_data;
+  test_exchange_t    *ghost_struct_data, *e;
+
+  /* Test C: don't use p4est user_data at all */
+
+  mirror_struct_data =
+    P4EST_ALLOC (test_exchange_t, ghost->mirrors.elem_count);
+  mirror_data = P4EST_ALLOC (void *, ghost->mirrors.elem_count);
+  for (zz = 0; zz < ghost->mirrors.elem_count; ++zz) {
+    q = p4est_quadrant_array_index (&ghost->mirrors, zz);
+    gnum = p4est->global_first_quadrant[p4est->mpirank] +
+      (p4est_gloidx_t) q->p.piggy3.local_num;
+    mirror_data[zz] = e = mirror_struct_data + zz;
+    e->gi = gnum;
+    e->ll = (long) gnum;
+    e->magic = TEST_EXCHANGE_MAGIC;
+  }
+
+  ghost_struct_data = P4EST_ALLOC (test_exchange_t, ghost->ghosts.elem_count);
+  p4est_ghost_exchange_custom_levels (p4est, ghost,
+                                      exchange_minlevel, exchange_maxlevel,
+                                      sizeof (test_exchange_t),
+                                      mirror_data, ghost_struct_data);
+
+  P4EST_FREE (mirror_data);
+  P4EST_FREE (mirror_struct_data);
+
+  gexcl = 0;
+  for (p = 0; p < p4est->mpisize; ++p) {
+    gincl = ghost->proc_offsets[p + 1];
+    gnum = p4est->global_first_quadrant[p];
+#ifdef P4EST_TEST_CHATTY
+    P4EST_LDEBUGF ("In test D for %d with %d %d\n", p, gexcl, gincl);
+#endif
+    for (gl = gexcl; gl < gincl; ++gl) {
+      q = p4est_quadrant_array_index (&ghost->ghosts, gl);
+      if (exchange_minlevel <= (int) q->level &&
+          (int) q->level <= exchange_maxlevel) {
+        e = ghost_struct_data + gl;
+        SC_CHECK_ABORT (gnum + (p4est_gloidx_t) q->p.piggy3.local_num ==
+                        e->gi, "Ghost exchange mismatch D1");
+        SC_CHECK_ABORT (gnum + (p4est_gloidx_t) q->p.piggy3.local_num ==
+                        (p4est_gloidx_t) e->ll, "Ghost exchange mismatch D2");
+        SC_CHECK_ABORT (e->magic == TEST_EXCHANGE_MAGIC,
+                        "Ghost exchange mismatch D3");
+      }
+    }
+    gexcl = gincl;
+  }
+  P4EST_ASSERT (gexcl == (p4est_locidx_t) ghost->ghosts.elem_count);
+  P4EST_FREE (ghost_struct_data);
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  sc_MPI_Comm         mpicomm;
+  p4est_t            *p4est;
+  p4est_connectivity_t *conn;
+  p4est_ghost_t      *ghost;
+  int                 num_cycles = 2;
+  int                 i;
+  p4est_lnodes_t     *lnodes;
+  int                 type;
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+#ifndef P4_TO_P8
+  conn = p4est_connectivity_new_moebius ();
+#else
+  conn = p8est_connectivity_new_rotcubes ();
+#endif
+
+  p4est = p4est_new (mpicomm, conn, 0, NULL, NULL);
+
+  /* refine to make the number of elements interesting */
+  p4est_refine (p4est, 1, refine_fn, NULL);
+
+  /* balance the forest */
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+
+  /* do a uniform partition */
+  p4est_partition (p4est, 0, NULL);
+
+  /* create the ghost layer */
+  ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+
+  /* test ghost data exchange */
+  test_exchange_A (p4est, ghost);
+  test_exchange_B (p4est, ghost);
+  test_exchange_C (p4est, ghost);
+  test_exchange_D (p4est, ghost);
+
+  for (i = 0; i < num_cycles; i++) {
+    /* expand and test that the ghost layer can still exchange data properly
+     * */
+    p4est_ghost_expand (p4est, ghost);
+    test_exchange_A (p4est, ghost);
+    test_exchange_B (p4est, ghost);
+    test_exchange_C (p4est, ghost);
+    test_exchange_D (p4est, ghost);
+  }
+
+  p4est_ghost_destroy (ghost);
+  /* repeate the cyle, but with lnodes */
+  /* create the ghost layer */
+  ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+  type = p4est_connect_type_int (ghost->btype);
+  lnodes = p4est_lnodes_new (p4est, ghost, -type);
+  p4est_ghost_support_lnodes (p4est, lnodes, ghost);
+  /* test ghost data exchange */
+  test_exchange_A (p4est, ghost);
+  test_exchange_B (p4est, ghost);
+  test_exchange_C (p4est, ghost);
+  test_exchange_D (p4est, ghost);
+
+  for (i = 0; i < num_cycles; i++) {
+    /* expand and test that the ghost layer can still exchange data properly
+     * */
+    p4est_ghost_expand_by_lnodes (p4est, lnodes, ghost);
+    test_exchange_A (p4est, ghost);
+    test_exchange_B (p4est, ghost);
+    test_exchange_C (p4est, ghost);
+    test_exchange_D (p4est, ghost);
+  }
+
+  /* clean up */
+  p4est_lnodes_destroy (lnodes);
+  p4est_ghost_destroy (ghost);
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (conn);
+
+  /* exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_ghost3.c b/test/test_ghost3.c
new file mode 100644
index 0000000..cc95561
--- /dev/null
+++ b/test/test_ghost3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_ghost2.c"
diff --git a/test/test_hash.c b/test/test_hash.c
new file mode 100644
index 0000000..f76b784
--- /dev/null
+++ b/test/test_hash.c
@@ -0,0 +1,130 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_bits.h>
+
+static unsigned
+int_hash_fn (const void *v, const void *u)
+{
+  return (unsigned) (unsigned long) v;
+}
+
+static int
+int_equal_fn (const void *v1, const void *v2, const void *u)
+{
+  return (long) v1 == (long) v2;
+}
+
+static void
+check_hash_array (void)
+{
+  sc_hash_array_t    *ha;
+
+  ha = sc_hash_array_new (sizeof (int), NULL, NULL, NULL);
+
+  sc_hash_array_destroy (ha);
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 i, k, inserted;
+  int                 i1, i2, i3;
+  void              **vv1, **vv2, **vv3;
+  void               *v1, *v2;
+  p4est_quadrant_t    q1, q2, q3;
+  p4est_quadrant_t   *f1, *f2, *f3;
+  sc_hash_t          *ihash;
+  sc_hash_t          *qhash;
+
+  for (k = 0; k < 3; ++k) {
+    ihash = sc_hash_new (int_hash_fn, int_equal_fn, NULL, NULL);
+
+    inserted = 0;
+    for (i = 0; i < 347; ++i) {
+      inserted +=
+        sc_hash_insert_unique (ihash, (void *) ((long) i % 91), NULL);
+    }
+    P4EST_VERBOSEF ("Integers inserted %d total %llu\n",
+                    inserted, (unsigned long long) ihash->elem_count);
+    SC_CHECK_ABORT (inserted == (int) ihash->elem_count, "Integer hash");
+
+    sc_hash_destroy (ihash);
+  }
+
+  qhash = sc_hash_new (p4est_quadrant_hash_fn, p4est_quadrant_equal_fn,
+                       NULL, NULL);
+
+  p4est_quadrant_set_morton (&q1, 3, 15);
+  p4est_quadrant_set_morton (&q2, 3, 18);
+  p4est_quadrant_set_morton (&q3, 3, 18);
+  q1.p.piggy1.owner_rank = 0;
+  q2.p.piggy1.owner_rank = 5;
+  q3.p.piggy1.owner_rank = 0;
+
+  f1 = f2 = f3 = NULL;
+  /* *INDENT-OFF* HORRIBLE indent bug */
+  i1 = sc_hash_insert_unique (qhash, &q1, &vv1);
+  f1 = (p4est_quadrant_t *) *vv1;
+  i2 = sc_hash_insert_unique (qhash, &q2, &vv2);
+  f2 = (p4est_quadrant_t *) *vv2;
+  i3 = sc_hash_insert_unique (qhash, &q3, &vv3);
+  f3 = (p4est_quadrant_t *) *vv3;
+  /* *INDENT-ON* */
+  P4EST_VERBOSEF ("Quadrants inserted %d %d %d total %llu\n",
+                  i1, i2, i3, (unsigned long long) qhash->elem_count);
+
+  SC_CHECK_ABORT (i1 + i2 + i3 == (int) qhash->elem_count, "Quadrant hash");
+  SC_CHECK_ABORT (f3 == &q2 && f3->p.piggy1.owner_rank == 5, "Insert return");
+
+  f1 = f2 = f3 = NULL;
+  p4est_quadrant_set_morton (&q1, 3, 19);
+  i1 = sc_hash_lookup (qhash, &q1, NULL);
+  i2 = sc_hash_lookup (qhash, &q2, NULL);
+  i3 = sc_hash_lookup (qhash, &q3, &vv3);
+  /* *INDENT-OFF* HORRIBLE indent bug */
+  f3 = (p4est_quadrant_t *) *vv3;
+  /* *INDENT-ON* */
+  P4EST_VERBOSEF ("Quadrants lookup %d %d %d total %llu\n",
+                  i1, i2, i3, (unsigned long long) qhash->elem_count);
+  SC_CHECK_ABORT (i1 == 0 && i2 == 1 && i3 == 1, "Quadrant lookup");
+  SC_CHECK_ABORT (f3 == &q2 && f3->p.piggy1.owner_rank == 5, "Lookup return");
+
+  f1 = f2 = f3 = NULL;
+  i1 = sc_hash_remove (qhash, &q1, &v1);
+  f1 = (p4est_quadrant_t *) v1;
+  i2 = sc_hash_remove (qhash, &q2, &v2);
+  f2 = (p4est_quadrant_t *) v2;
+  i3 = sc_hash_remove (qhash, &q3, NULL);
+  SC_CHECK_ABORT (i1 == 0 && i2 == 1 && i3 == 0, "Quadrant remove");
+  SC_CHECK_ABORT (f2 == &q2 && f2->p.piggy1.owner_rank == 5, "Remove return");
+  f2 = f1;
+
+  sc_hash_destroy (qhash);
+
+  check_hash_array ();
+
+  sc_finalize ();
+
+  return 0;
+}
diff --git a/test/test_iterate2.c b/test/test_iterate2.c
new file mode 100644
index 0000000..5adc130
--- /dev/null
+++ b/test/test_iterate2.c
@@ -0,0 +1,1051 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_ghost.h>
+#include <p4est_iterate.h>
+#else
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_ghost.h>
+#include <p8est_iterate.h>
+#endif
+
+#ifndef P4_TO_P8
+static int          refine_level = 5;
+#else
+static int          refine_level = 3;
+#endif
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  if (which_tree == 2 || which_tree == 3) {
+    return 0;
+  }
+
+  cid = p4est_quadrant_child_id (quadrant);
+
+  if (cid == P4EST_CHILDREN - 1 ||
+      (quadrant->x >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2) &&
+       quadrant->y >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#ifdef P4_TO_P8
+       && quadrant->z >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#endif
+      )) {
+    return 1;
+  }
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && cid == 2) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_QUADRANT_LEN (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->y >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+#ifndef P4_TO_P8
+static int          face_offset = 1;
+static int          corner_offset = 5;
+static int          checks_per_quad = 9;
+static int          check_to_type[9] = { 2, 1, 1, 1, 1, 0, 0, 0, 0 };
+#else
+static int          face_offset = 1;
+static int          edge_offset = 7;
+static int          corner_offset = 19;
+static int          checks_per_quad = 27;
+static int          check_to_type[27] = { 3,
+  2, 2, 2, 2, 2, 2,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  0, 0, 0, 0, 0, 0, 0, 0
+};
+#endif
+
+typedef struct iter_data
+{
+  int8_t              count_volume;
+  int8_t              count_face;
+  int8_t              ghost_face;
+#ifdef P4_TO_P8
+  int8_t              count_edge;
+  int8_t              ghost_edge;
+#endif
+  int8_t              count_corner;
+  int8_t              ghost_corner;
+  int                *checks;
+}
+iter_data_t;
+
+#if 0
+/*@unused@*/
+static void
+volume_do_nothing (p4est_iter_volume_info_t * info, void *data)
+{
+};
+
+/*@unused@*/
+static void
+face_do_nothing (p4est_iter_face_info_t * info, void *data)
+{
+};
+
+#ifdef P4_TO_P8
+/*@unused@*/
+static void
+edge_do_nothing (p8est_iter_edge_info_t * info, void *data)
+{
+};
+#endif
+
+/*@unused@*/
+static void
+corner_do_nothing (p4est_iter_corner_info_t * info, void *data)
+{
+};
+#endif /* 0 */
+
+static              int8_t
+test_corner_side (p4est_t * p4est, p4est_iter_corner_side_t * side,
+                  void *data)
+{
+  int                 corner = (int) side->corner;
+  p4est_quadrant_t   *q = side->quad;
+  iter_data_t        *iter_data = (iter_data_t *) data;
+  int                *checks = iter_data->checks;
+  p4est_locidx_t      qid = side->quadid;
+  p4est_topidx_t      t = side->treeid;
+  p4est_tree_t       *tree;
+  p4est_locidx_t      offset;
+
+  tree = p4est_tree_array_index (p4est->trees, t);
+  offset = tree->quadrants_offset;
+
+  q = side->quad;
+  if (q == NULL) {
+    SC_CHECK_ABORT (!(iter_data->ghost_corner), "Iterate: empty corner side");
+    return 1;
+  }
+  else {
+    if (!side->is_ghost && iter_data->count_corner) {
+      qid += offset;
+      checks[qid * checks_per_quad + corner + corner_offset]++;
+    }
+  }
+
+  return side->is_ghost;
+}
+
+static              int8_t
+quad_is_in_corner_sides (p4est_quadrant_t * q, p4est_topidx_t t,
+                         sc_array_t * sides)
+{
+  size_t              zz;
+  size_t              zcount = sides->elem_count;
+  p4est_iter_corner_side_t *cside;
+  p4est_quadrant_t   *r;
+
+  for (zz = 0; zz < zcount; zz++) {
+    cside = p4est_iter_cside_array_index (sides, zz);
+    if (cside->treeid == t) {
+      r = cside->quad;
+      if (r == NULL) {
+        continue;
+      }
+      if (p4est_quadrant_is_equal (q, r)) {
+        return 1;
+      }
+      if (p4est_quadrant_is_ancestor (q, r)) {
+        return 1;
+      }
+    }
+  }
+
+  return 0;
+}
+
+static void
+test_corner_boundary (p4est_iter_corner_info_t * info)
+{
+  int                 c, f, dir, which;
+  int                 k, count;
+  p4est_qcoord_t      end, xyz[P4EST_DIM];
+  p4est_iter_corner_side_t *cside;
+  p4est_quadrant_t   *q;
+
+  SC_CHECK_ABORT (info->sides.elem_count > 0, "Empty corner iteration");
+  cside = (p4est_iter_corner_side_t *) sc_array_index (&info->sides, 0);
+  c = cside->corner;
+
+  if (!info->tree_boundary) {
+    SC_CHECK_ABORT (c == P4EST_CHILDREN - 1, "Not the lowest corner");
+    return;
+  }
+
+  /* grab information about this quadrant */
+  q = cside->quad;
+  if (q == NULL) {
+    SC_CHECK_ABORT (cside->is_ghost && cside->quadid == -1,
+                    "Not a corner ghost");
+    return;
+  }
+  end = P4EST_LAST_OFFSET (q->level);
+  xyz[0] = q->x;
+  xyz[1] = q->y;
+#ifdef P4_TO_P8
+  xyz[2] = q->z;
+#endif
+
+  /* check how many tree faces it is touching */
+  count = 0;
+  for (k = 0; k < P4EST_DIM; ++k) {
+    f = p4est_corner_faces[c][k];
+    dir = f >> 1;
+    which = f & 1;
+    count += (which == 0 && xyz[dir] == 0) || (which == 1 && xyz[dir] == end);
+  }
+  if (info->tree_boundary == P4EST_CONNECT_CORNER) {
+    /* we are at a true inter-tree corner */
+    SC_CHECK_ABORT (count == P4EST_DIM, "Not a tree boundary corner");
+  }
+#ifdef P4_TO_P8
+  else if (info->tree_boundary == P8EST_CONNECT_EDGE) {
+    /* we are a corner inside an inter-tree edge */
+    SC_CHECK_ABORT (count == 2, "Not a tree edge boundary corner");
+  }
+#endif
+  else if (info->tree_boundary == P4EST_CONNECT_FACE) {
+    /* we are a corner inside an inter-tree face */
+    SC_CHECK_ABORT (count == 1, "Not a tree face boundary corner");
+  }
+  else {
+    SC_ABORT_NOT_REACHED ();
+  }
+}
+
+static void
+test_corner_adjacency (p4est_iter_corner_info_t * info, void *data)
+{
+  int                 i, j;
+  int8_t              has_local = 0;
+  int                 limit = (int) info->sides.elem_count;
+  p4est_iter_corner_side_t *cside;
+  p4est_quadrant_t    tempq, tempr;
+  int                 c, f;
+  p4est_topidx_t      t, nt;
+  sc_array_t          quads, treeids;
+  p4est_connectivity_t *conn = info->p4est->connectivity;
+  size_t              zz;
+  p4est_quadrant_t   *ptemp;
+#ifdef P4_TO_P8
+  int                 e;
+#endif
+  int8_t              min_level = P4EST_QMAXLEVEL;
+  iter_data_t        *iter_data = (iter_data_t *) data;
+
+  test_corner_boundary (info);
+
+  for (i = 0; i < limit; i++) {
+    cside = p4est_iter_cside_array_index_int (&info->sides, i);
+    has_local = (!test_corner_side (info->p4est, cside, data) || has_local);
+    if (cside->quad != NULL) {
+      min_level = (cside->quad->level < min_level) ? cside->quad->level :
+        min_level;
+    }
+  }
+  SC_CHECK_ABORT (has_local, "Iterate: non local corner");
+
+  sc_array_init (&quads, sizeof (p4est_quadrant_t));
+  sc_array_init (&treeids, sizeof (p4est_topidx_t));
+  for (i = 0; i < limit; i++) {
+    cside = p4est_iter_cside_array_index_int (&info->sides, i);
+    if (cside->quad == NULL) {
+      continue;
+    }
+    c = (int) cside->corner;
+    t = cside->treeid;
+    tempq = *(cside->quad);
+    tempq.x &= ((p4est_qcoord_t) - 1) << (P4EST_MAXLEVEL - min_level);
+    tempq.y &= ((p4est_qcoord_t) - 1) << (P4EST_MAXLEVEL - min_level);
+#ifdef P4_TO_P8
+    tempq.z &= ((p4est_qcoord_t) - 1) << (P4EST_MAXLEVEL - min_level);
+#endif
+    tempq.level = min_level;
+    P4EST_ASSERT (p4est_quadrant_is_valid (&tempq));
+    for (j = 0; j < P4EST_DIM; j++) {
+      f = p4est_corner_faces[c][j];
+      nt = p4est_quadrant_face_neighbor_extra (&tempq, t, f, &tempr, NULL,
+                                               conn);
+      if (nt == -1) {
+        continue;
+      }
+      if (!quad_is_in_corner_sides (&tempr, nt, &(info->sides))) {
+        SC_CHECK_ABORT (!(!cside->is_ghost && iter_data->ghost_face),
+                        "Iterate: quad missing corner neighbor");
+      }
+    }
+#ifdef P4_TO_P8
+    for (j = 0; j < 3; j++) {
+      e = p8est_corner_edges[c][j];
+      sc_array_resize (&quads, 0);
+      sc_array_resize (&treeids, 0);
+      p8est_quadrant_edge_neighbor_extra (&tempq, t, e, &quads, &treeids,
+                                          NULL, conn);
+      for (zz = 0; zz < quads.elem_count; zz++) {
+        ptemp = p4est_quadrant_array_index (&quads, zz);
+        nt = *((p4est_topidx_t *) sc_array_index (&treeids, zz));
+        if (!quad_is_in_corner_sides (ptemp, nt, &(info->sides))) {
+          SC_CHECK_ABORT (!(!cside->is_ghost && iter_data->ghost_edge),
+                          "Iterate: quad missing corner neighbor");
+        }
+      }
+    }
+#endif
+    sc_array_resize (&quads, 0);
+    sc_array_resize (&treeids, 0);
+    p4est_quadrant_corner_neighbor_extra (&tempq, t, c, &quads, &treeids,
+                                          NULL, conn);
+    for (zz = 0; zz < quads.elem_count; zz++) {
+      ptemp = p4est_quadrant_array_index (&quads, zz);
+      nt = *((p4est_topidx_t *) sc_array_index (&treeids, zz));
+      if (!quad_is_in_corner_sides (ptemp, nt, &(info->sides))) {
+        SC_CHECK_ABORT (!(iter_data->ghost_corner),
+                        "Iterate: quad missing corner neighbor");
+      }
+    }
+
+  }
+
+  sc_array_reset (&quads);
+  sc_array_reset (&treeids);
+}
+
+#ifdef P4_TO_P8
+static              int8_t
+test_edge_side (p4est_t * p4est, p8est_iter_edge_side_t * side, void *data)
+{
+  int                 i;
+  int                 edge = (int) side->edge;
+  int                 child_id, opp_id;
+  int8_t              has_local = 0;
+  p4est_quadrant_t   *q;
+  p4est_quadrant_t    tempq;
+  iter_data_t        *iter_data = (iter_data_t *) data;
+  int                *checks = iter_data->checks;
+  p4est_locidx_t      qid;
+  p4est_topidx_t      t = side->treeid;
+  p4est_tree_t       *tree;
+  p4est_locidx_t      offset;
+  int                 quad_count = 0;
+
+  tree = p4est_tree_array_index (p4est->trees, t);
+  offset = tree->quadrants_offset;
+
+  if (!side->is_hanging) {
+    q = side->is.full.quad;
+    if (q == NULL) {
+      SC_CHECK_ABORT (!(iter_data->ghost_edge),
+                      "Iterate: full edge side missing quadrant");
+      return 1;
+    }
+    if (!side->is.full.is_ghost && iter_data->count_edge) {
+      qid = side->is.full.quadid + offset;
+      checks[qid * checks_per_quad + edge + edge_offset]++;
+    }
+    return side->is.full.is_ghost;
+  }
+  else {
+    for (i = 0; i < 2; i++) {
+      q = side->is.hanging.quad[i];
+      if (q == NULL) {
+        SC_CHECK_ABORT (!(iter_data->ghost_corner),
+                        "Iterate: hanging edge missing quadrant");
+        continue;
+      }
+      quad_count++;
+      child_id = p4est_quadrant_child_id (q);
+      SC_CHECK_ABORT (p8est_edge_corners[edge][i] == child_id,
+                      "Iterate: edge side ordering");
+      opp_id = p8est_edge_corners[edge][1 - i];
+      if (!side->is.hanging.is_ghost[i]) {
+        has_local = 1;
+        qid = side->is.hanging.quadid[i] + offset;
+        if (iter_data->count_edge) {
+          checks[qid * checks_per_quad + edge + edge_offset]++;
+        }
+        if (iter_data->count_corner) {
+          checks[qid * checks_per_quad + opp_id + corner_offset]++;
+        }
+      }
+    }
+    switch (quad_count) {
+    case 0:
+      SC_CHECK_ABORT (!(iter_data->ghost_edge),
+                      "Iterate: hanging edge missing all quadrants");
+      break;
+    case 1:
+      SC_CHECK_ABORT (!(has_local && iter_data->ghost_face),
+                      "Iterate: hanging edge missing quadrant");
+      break;
+    default:
+      q = side->is.hanging.quad[0];
+      p4est_quadrant_parent (q, &tempq);
+      q = side->is.hanging.quad[1];
+      SC_CHECK_ABORT (p4est_quadrant_is_parent (&tempq, q),
+                      "Iterate: non siblings share edge");
+      break;
+    }
+    return !has_local;
+  }
+}
+
+static              int8_t
+quad_is_in_edge_sides (p8est_quadrant_t * q, p4est_topidx_t t,
+                       sc_array_t * sides)
+{
+  size_t              zz;
+  size_t              zcount = sides->elem_count;
+  p8est_iter_edge_side_t *eside;
+
+  for (zz = 0; zz < zcount; zz++) {
+    eside = p8est_iter_eside_array_index (sides, zz);
+    if (eside->treeid == t) {
+      if (!eside->is_hanging) {
+        if (eside->is.full.quad != NULL) {
+          if (p4est_quadrant_is_equal (q, eside->is.full.quad)) {
+            return 1;
+          }
+        }
+      }
+      else if (eside->is.hanging.quad[0] != NULL) {
+        if (p4est_quadrant_is_parent (q, eside->is.hanging.quad[0])) {
+          return 1;
+        }
+      }
+      else if (eside->is.hanging.quad[1] != NULL) {
+        if (p4est_quadrant_is_parent (q, eside->is.hanging.quad[1])) {
+          return 1;
+        }
+      }
+    }
+  }
+  return 0;
+}
+
+static void
+test_edge_boundary (p8est_iter_edge_info_t * info)
+{
+  int                 e, f, dir, which;
+  int                 k, count;
+  p4est_qcoord_t      end, xyz[P4EST_DIM];
+  p8est_iter_edge_side_t *eside;
+  p4est_quadrant_t   *q;
+
+  SC_CHECK_ABORT (info->sides.elem_count > 0, "Empty edge iteration");
+  eside = (p8est_iter_edge_side_t *) sc_array_index (&info->sides, 0);
+  e = eside->edge;
+
+  if (!info->tree_boundary) {
+    SC_CHECK_ABORT (e & 1, "Not the lowest edge");
+    return;
+  }
+
+  /* grab information about this quadrant */
+  q = eside->is_hanging ? eside->is.hanging.quad[0] : eside->is.full.quad;
+  if (q == NULL) {
+    return;
+  }
+  end = P4EST_LAST_OFFSET (q->level);
+  xyz[0] = q->x;
+  xyz[1] = q->y;
+  xyz[2] = q->z;
+
+  /* check how many tree faces it is touching */
+  count = 0;
+  for (k = 0; k < 2; ++k) {
+    f = p8est_edge_faces[e][k];
+    dir = f >> 1;
+    which = f & 1;
+    count += (which == 0 && xyz[dir] == 0) || (which == 1 && xyz[dir] == end);
+  }
+  if (info->tree_boundary == P8EST_CONNECT_EDGE) {
+    /* we are at a true inter-tree edge */
+    SC_CHECK_ABORT (count == 2, "Not a tree boundary edge");
+  }
+  else if (info->tree_boundary == P4EST_CONNECT_FACE) {
+    /* we are an edge inside an inter-tree face */
+    SC_CHECK_ABORT (count == 1, "Not a tree face boundary edge");
+  }
+  else {
+    SC_ABORT_NOT_REACHED ();
+  }
+}
+
+static void
+test_edge_adjacency (p8est_iter_edge_info_t * info, void *data)
+{
+  int                 i, j;
+  int8_t              has_local = 0;
+  int                 limit = (int) info->sides.elem_count;
+  p8est_iter_edge_side_t *eside;
+  p8est_quadrant_t    tempq, tempr;
+  int                 e, f;
+  p4est_topidx_t      t, nt;
+  sc_array_t          quads, treeids;
+  p8est_connectivity_t *conn = info->p4est->connectivity;
+  size_t              zz;
+  p8est_quadrant_t   *ptemp;
+  iter_data_t        *iter_data = (iter_data_t *) data;
+
+  test_edge_boundary (info);
+
+  sc_array_init (&quads, sizeof (p8est_quadrant_t));
+  sc_array_init (&treeids, sizeof (p4est_topidx_t));
+  for (i = 0; i < limit; i++) {
+    eside = p8est_iter_eside_array_index_int (&info->sides, i);
+    has_local = (!test_edge_side (info->p4est, eside, data) || has_local);
+  }
+  SC_CHECK_ABORT (has_local, "Iterate: non local edge");
+
+  for (i = 0; i < limit; i++) {
+    eside = p8est_iter_eside_array_index_int (&(info->sides), i);
+    if (!eside->is_hanging) {
+      if (eside->is.full.quad == NULL) {
+        continue;
+      }
+      tempq = *(eside->is.full.quad);
+      has_local = !eside->is.full.is_ghost;
+    }
+    else {
+      if (eside->is.hanging.quad[0] != NULL) {
+        p4est_quadrant_parent (eside->is.hanging.quad[0], &tempq);
+        has_local = !eside->is.hanging.is_ghost[0];
+      }
+      else {
+        if (eside->is.hanging.quad[1] == NULL) {
+          continue;
+        }
+        p4est_quadrant_parent (eside->is.hanging.quad[1], &tempq);
+        has_local = !eside->is.hanging.is_ghost[1];
+      }
+    }
+    e = (int) eside->edge;
+    t = eside->treeid;
+    for (j = 0; j < 2; j++) {
+      f = p8est_edge_faces[e][j];
+      nt = p8est_quadrant_face_neighbor_extra (&tempq, t, f, &tempr, NULL,
+                                               conn);
+      if (nt == -1) {
+        continue;
+      }
+      if (!quad_is_in_edge_sides (&tempr, nt, &(info->sides))) {
+        SC_CHECK_ABORT (!(has_local && iter_data->ghost_face),
+                        "Iterate: quad missing edge neighbor");
+      }
+    }
+    sc_array_resize (&quads, 0);
+    sc_array_resize (&treeids, 0);
+    p8est_quadrant_edge_neighbor_extra (&tempq, t, e, &quads, &treeids, NULL,
+                                        conn);
+    for (zz = 0; zz < quads.elem_count; zz++) {
+      ptemp = p4est_quadrant_array_index (&quads, zz);
+      nt = *((p4est_topidx_t *) sc_array_index (&treeids, zz));
+      if (!quad_is_in_edge_sides (ptemp, nt, &(info->sides))) {
+        SC_CHECK_ABORT (!(iter_data->ghost_edge),
+                        "Iterate: quad missing edge neighbor");
+      }
+    }
+    sc_array_reset (&quads);
+    sc_array_reset (&treeids);
+  }
+}
+#endif
+
+static              int8_t
+test_face_side (p4est_t * p4est, p4est_iter_face_side_t * side, void *data)
+{
+  int                 i;
+  int                 face = (int) side->face;
+  int                 child_id, opp_id;
+  int8_t              has_local = 0;
+  p4est_quadrant_t   *q;
+  p4est_quadrant_t    tempq;
+  iter_data_t        *iter_data = (iter_data_t *) data;
+  int                *checks = iter_data->checks;
+  p4est_locidx_t      qid;
+#ifdef P4_TO_P8
+  int                 edge;
+  int                 dir = face / 2;
+  int                 j;
+#endif
+  p4est_topidx_t      t = side->treeid;
+  p4est_tree_t       *tree;
+  p4est_locidx_t      offset;
+  int                 quad_count = 0;
+
+  tree = p4est_tree_array_index (p4est->trees, t);
+  offset = tree->quadrants_offset;
+
+  if (!side->is_hanging) {
+    q = side->is.full.quad;
+    if (q == NULL) {
+      SC_CHECK_ABORT (!(iter_data->ghost_face),
+                      "Iterate: full face side missing quadrant");
+      return 1;
+    }
+    if (!side->is.full.is_ghost && iter_data->count_face) {
+      qid = side->is.full.quadid + offset;
+      checks[qid * checks_per_quad + face + face_offset]++;
+    }
+    return side->is.full.is_ghost;
+  }
+  else {
+    for (i = 0; i < P4EST_CHILDREN / 2; i++) {
+      q = side->is.hanging.quad[i];
+      if (q == NULL) {
+#ifndef P4_TO_P8
+        SC_CHECK_ABORT (!(iter_data->ghost_face),
+                        "Iterate: hanging face side missing quadrant");
+#else
+        SC_CHECK_ABORT (!(iter_data->ghost_edge),
+                        "Iterate: hanging face side missing quadrant");
+#endif
+        continue;
+      }
+      quad_count++;
+      child_id = p4est_quadrant_child_id (q);
+      SC_CHECK_ABORT (p4est_face_corners[face][i] == child_id,
+                      "Iterate: face side ordering");
+      opp_id = p4est_face_corners[face][P4EST_CHILDREN / 2 - 1 - i];
+      if (!side->is.hanging.is_ghost[i]) {
+        has_local = 1;
+        qid = side->is.hanging.quadid[i] + offset;
+        if (iter_data->count_face) {
+          checks[qid * checks_per_quad + face + face_offset]++;
+        }
+        if (iter_data->count_corner) {
+          checks[qid * checks_per_quad + opp_id + corner_offset]++;
+        }
+#ifdef P4_TO_P8
+        if (iter_data->count_edge) {
+          for (j = 1; j <= 2; j++) {
+            edge = p8est_corner_edges[opp_id][(dir + j) % 3];
+            checks[qid * checks_per_quad + edge + edge_offset]++;
+          }
+        }
+#endif
+      }
+    }
+    switch (quad_count) {
+    case 0:
+    case 1:
+      SC_CHECK_ABORT (!(iter_data->ghost_face),
+                      "Iterate: hanging face side missing all quadrants");
+      break;
+#ifdef P4_TO_P8
+    case 2:
+      SC_CHECK_ABORT (!(iter_data->ghost_face),
+                      "Iterate: hanging face side missing all quadrants");
+      break;
+    case 3:
+      SC_CHECK_ABORT (!(iter_data->ghost_edge),
+                      "Iterate: hanging face missing quadrant");
+      break;
+#endif
+    default:
+      q = side->is.hanging.quad[0];
+#ifdef P4_TO_P8
+      if (q == NULL) {
+        q = side->is.hanging.quad[1];
+      }
+#endif
+      p4est_quadrant_parent (q, &tempq);
+      for (i = 1; i < P4EST_CHILDREN / 2; i++) {
+        q = side->is.hanging.quad[i];
+        if (q != NULL) {
+          SC_CHECK_ABORT (p4est_quadrant_is_parent (&tempq, q),
+                          "Iterate: non siblings share face");
+        }
+      }
+      break;
+    }
+    return !has_local;
+  }
+}
+
+static void
+test_face_boundary (p4est_iter_face_info_t * info)
+{
+  int                 f;
+  int                 result;
+  p4est_qcoord_t      end;
+  p4est_iter_face_side_t *fside;
+  p4est_quadrant_t   *q;
+
+  SC_CHECK_ABORT (info->sides.elem_count > 0, "Empty face iteration");
+  fside = (p4est_iter_face_side_t *) sc_array_index (&info->sides, 0);
+  f = fside->face;
+
+  if (!info->tree_boundary) {
+    SC_CHECK_ABORT (f & 1, "Not the lowest face");
+  }
+  else {
+    SC_CHECK_ABORT (info->tree_boundary == P4EST_CONNECT_FACE, "Not a face");
+    q = fside->is_hanging ? fside->is.hanging.quad[0] : fside->is.full.quad;
+    if (q == NULL) {
+      return;
+    }
+    end = P4EST_LAST_OFFSET (q->level);
+
+    result =
+      (f == 0 && q->x == 0) || (f == 1 && q->x == end) ||
+      (f == 2 && q->y == 0) || (f == 3 && q->y == end) ||
+#ifdef P4_TO_P8
+      (f == 4 && q->z == 0) || (f == 5 && q->z == end) ||
+#endif
+      0;
+    SC_CHECK_ABORT (result, "Not a tree boundary face");
+  }
+}
+
+static void
+test_face_adjacency (p4est_iter_face_info_t * info, void *data)
+{
+  int                 i, j;
+  int8_t              is_ghost[2];
+  int                 limit = (int) info->sides.elem_count;
+  p4est_iter_face_side_t *fside;
+  p4est_quadrant_t    tempq[2], tempr[2];
+  int                 face[2];
+  p4est_topidx_t      treeid[2], nt[2];
+
+  test_face_boundary (info);
+
+  for (i = 0; i < 2; i++) {
+    is_ghost[i] = 1;
+  }
+  for (i = 0; i < limit; i++) {
+    fside = p4est_iter_fside_array_index_int (&info->sides, i);
+    is_ghost[i] = test_face_side (info->p4est, fside, data);
+  }
+  SC_CHECK_ABORT (!(is_ghost[0] && is_ghost[1]), "Iterate: non local face");
+
+  for (i = 0; i < limit; i++) {
+    fside = p4est_iter_fside_array_index_int (&info->sides, i);
+    face[i] = (int) fside->face;
+    treeid[i] = fside->treeid;
+    if (!fside->is_hanging) {
+      if (fside->is.full.quad == NULL) {
+        return;
+      }
+      tempq[i] = *(fside->is.full.quad);
+    }
+    else {
+      for (j = 0; j < P4EST_CHILDREN / 2; j++) {
+        if (fside->is.hanging.quad[0] != NULL) {
+          p4est_quadrant_parent (fside->is.hanging.quad[0], &(tempq[i]));
+          break;
+        }
+      }
+      if (j == P4EST_CHILDREN / 2) {
+        return;
+      }
+    }
+    nt[1 - i] =
+      p4est_quadrant_face_neighbor_extra (&(tempq[i]), treeid[i], face[i],
+                                          &(tempr[1 - i]), NULL,
+                                          info->p4est->connectivity);
+  }
+  if (limit == 2) {
+    for (i = 0; i < 2; i++) {
+      SC_CHECK_ABORT (nt[i] == treeid[i], "Iterate: face tree mismatch");
+      SC_CHECK_ABORT (p4est_quadrant_is_equal (&(tempq[i]), &(tempr[i])),
+                      "Iterate: face neighbor mismatch");
+    }
+  }
+  else {
+    SC_CHECK_ABORT (nt[1] == -1,
+                    "Iterate: non boundary face without neighbor");
+    fside = p4est_iter_fside_array_index_int (&info->sides, 0);
+    SC_CHECK_ABORT (!fside->is_hanging,
+                    "Iterate: hanging face without neighbor");
+  }
+}
+
+static void
+test_volume_adjacency (p4est_iter_volume_info_t * info, void *data)
+{
+  iter_data_t        *iter_data = (iter_data_t *) data;
+  int                *checks = iter_data->checks;
+  p4est_locidx_t      qid = info->quadid;
+  p4est_topidx_t      t = info->treeid;
+  p4est_tree_t       *tree = p4est_tree_array_index (info->p4est->trees, t);
+
+  SC_CHECK_ABORT (info->quad != NULL, "Iterate: missing volume quad");
+  if (iter_data->count_volume) {
+    qid += tree->quadrants_offset;
+    checks[qid * checks_per_quad]++;
+  }
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 mpisize, mpirank;
+  p4est_t            *p4est;
+  p4est_connectivity_t *connectivity;
+  p4est_locidx_t      num_quads, li;
+  p4est_locidx_t      num_checks;
+  int                *checks;
+  p4est_ghost_t      *ghost_layer;
+  int                 ntests;
+  int                 i, j, k;
+  iter_data_t         iter_data;
+  p4est_iter_volume_t iter_volume;
+  p4est_iter_face_t   iter_face;
+#ifdef P4_TO_P8
+  p8est_iter_edge_t   iter_edge;
+#endif
+  p4est_iter_corner_t iter_corner;
+  int                 volume_count;
+  int                 face_count;
+#ifdef P4_TO_P8
+  int                 edge_count;
+#endif
+  int                 corner_count;
+
+  ntests = 3;
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  for (i = 0; i < ntests; i++) {
+    /* create connectivity and forest structures */
+    switch (i) {
+#ifndef P4_TO_P8
+    case 0:
+      connectivity = p4est_connectivity_new_moebius ();
+      break;
+    case 1:
+      connectivity = p4est_connectivity_new_star ();
+      break;
+    default:
+      connectivity = p4est_connectivity_new_periodic ();
+      break;
+#else
+    case 0:
+      connectivity = p8est_connectivity_new_periodic ();
+      break;
+    case 1:
+      connectivity = p8est_connectivity_new_rotwrap ();
+      break;
+    default:
+      connectivity = p8est_connectivity_new_rotcubes ();
+      break;
+#endif
+    }
+    p4est = p4est_new_ext (mpicomm, connectivity, 15, 0, 0, 0, NULL, NULL);
+
+    /* refine to make the number of elements interesting */
+    p4est_refine (p4est, 1, refine_fn, NULL);
+
+    /* balance the forest */
+    /* TODO: use BALANCE_FACE/EDGE when that is known to work */
+    p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+
+    /* do a uniform partition */
+    p4est_partition (p4est, 0, NULL);
+
+    num_quads = p4est->local_num_quadrants;
+    num_checks = checks_per_quad * num_quads;
+    checks = P4EST_ALLOC_ZERO (int, num_checks);
+
+    iter_data.checks = checks;
+
+    volume_count = 0;
+    face_count = 0;
+#ifdef P4_TO_P8
+    edge_count = 0;
+#endif
+    corner_count = 0;
+
+    for (j = 0; j <= P4EST_DIM; j++) {
+
+      iter_data.count_volume = 1;
+      iter_data.count_face = (j > 0);
+#ifdef P4_TO_P8
+      iter_data.count_edge = (j > 1);
+#endif
+      iter_data.count_corner = (j == P4EST_DIM);
+
+      for (k = 0; k <= P4EST_DIM; k++) {
+        switch (k) {
+        case 0:
+          ghost_layer = NULL;
+          iter_data.ghost_face = 0;
+#ifdef P4_TO_P8
+          iter_data.ghost_edge = 0;
+#endif
+          iter_data.ghost_corner = 0;
+          break;
+#ifndef P4_TO_P8
+        case 1:
+          ghost_layer = p4est_ghost_new (p4est, P4EST_CONNECT_FACE);
+          iter_data.ghost_face = 1;
+          iter_data.ghost_corner = 0;
+          break;
+        default:
+          ghost_layer = p4est_ghost_new (p4est, P4EST_CONNECT_CORNER);
+          iter_data.ghost_face = 1;
+          iter_data.ghost_corner = 1;
+          break;
+#else
+        case 1:
+          ghost_layer = p4est_ghost_new (p4est, P8EST_CONNECT_FACE);
+          iter_data.ghost_face = 1;
+          iter_data.ghost_edge = 0;
+          iter_data.ghost_corner = 0;
+          break;
+        case 2:
+          ghost_layer = p4est_ghost_new (p4est, P8EST_CONNECT_EDGE);
+          iter_data.ghost_face = 1;
+          iter_data.ghost_edge = 1;
+          iter_data.ghost_corner = 0;
+          break;
+        default:
+          ghost_layer = p4est_ghost_new (p4est, P8EST_CONNECT_CORNER);
+          iter_data.ghost_face = 1;
+          iter_data.ghost_edge = 1;
+          iter_data.ghost_corner = 1;
+          break;
+#endif
+        }
+
+        if (iter_data.count_volume) {
+          iter_volume = test_volume_adjacency;
+          volume_count++;
+        }
+        else {
+          iter_volume = NULL;
+        }
+        if (iter_data.count_face) {
+          iter_face = test_face_adjacency;
+          face_count++;
+        }
+        else {
+          iter_face = NULL;
+        }
+#ifdef P4_TO_P8
+        if (iter_data.count_edge) {
+          iter_edge = test_edge_adjacency;
+          edge_count++;
+        }
+        else {
+          iter_edge = NULL;
+        }
+#endif
+        if (iter_data.count_corner) {
+          iter_corner = test_corner_adjacency;
+          corner_count++;
+        }
+        else {
+          iter_corner = NULL;
+        }
+
+        P4EST_GLOBAL_PRODUCTIONF ("Begin adjacency test %d:%d:%d\n", i, j, k);
+
+        p4est_iterate (p4est, ghost_layer, &iter_data, iter_volume, iter_face,
+#ifdef P4_TO_P8
+                       iter_edge,
+#endif
+                       iter_corner);
+
+        for (li = 0; li < num_checks; li++) {
+          switch (check_to_type[li % checks_per_quad]) {
+          case P4EST_DIM:
+            SC_CHECK_ABORT (checks[li] == volume_count,
+                            "Iterate: completion check");
+            break;
+          case (P4EST_DIM - 1):
+            SC_CHECK_ABORT (checks[li] == face_count,
+                            "Iterate: completion check");
+            break;
+#ifdef P4_TO_P8
+          case 1:
+            SC_CHECK_ABORT (checks[li] == edge_count,
+                            "Iterate: completion check");
+            break;
+#endif
+          default:
+            SC_CHECK_ABORT (checks[li] == corner_count,
+                            "Iterate: completion check");
+          }
+        }
+        /* clean up */
+        if (k > 0) {
+          p4est_ghost_destroy (ghost_layer);
+        }
+      }
+    }
+    P4EST_FREE (checks);
+
+    p4est_destroy (p4est);
+    p4est_connectivity_destroy (connectivity);
+    P4EST_GLOBAL_PRODUCTIONF ("End adjacency test %d\n", i);
+  }
+
+  /* exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
+
+/* EOF test_iterate2.c */
diff --git a/test/test_iterate3.c b/test/test_iterate3.c
new file mode 100644
index 0000000..f7c4b39
--- /dev/null
+++ b/test/test_iterate3.c
@@ -0,0 +1,27 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_iterate2.c"
+
+/* EOF test_iterate3.c */
diff --git a/test/test_join2.c b/test/test_join2.c
new file mode 100644
index 0000000..55d5fcb
--- /dev/null
+++ b/test/test_join2.c
@@ -0,0 +1,64 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2013 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_connectivity.h>
+#else
+#include <p8est_connectivity.h>
+#endif
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  p4est_connectivity_t *conn1, *conn2;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+#ifndef P4_TO_P8
+  conn1 = p4est_connectivity_new_unitsquare ();
+  conn2 = p4est_connectivity_new_rotwrap ();
+#else
+  conn1 = p8est_connectivity_new_unitcube ();
+  conn2 = p8est_connectivity_new_rotwrap ();
+#endif
+
+  p4est_connectivity_join_faces (conn1, 0, 0, 0, 1, 0);
+  p4est_connectivity_join_faces (conn1, 0, 0, P4EST_FACES - 2,
+                                 P4EST_FACES - 1, 1);
+
+  SC_CHECK_ABORT (p4est_connectivity_is_equivalent (conn1, conn2),
+                  "rotwrap not reproduced");
+
+  p4est_connectivity_destroy (conn1);
+  p4est_connectivity_destroy (conn2);
+
+  sc_finalize ();
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+  return 0;
+}
diff --git a/test/test_join3.c b/test/test_join3.c
new file mode 100644
index 0000000..13824ed
--- /dev/null
+++ b/test/test_join3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2013 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_join2.c"
diff --git a/test/test_lnodes2.c b/test/test_lnodes2.c
new file mode 100644
index 0000000..94060ce
--- /dev/null
+++ b/test/test_lnodes2.c
@@ -0,0 +1,984 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_ghost.h>
+#include <p4est_lnodes.h>
+#else
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_ghost.h>
+#include <p8est_lnodes.h>
+#endif
+
+#ifndef P4_TO_P8
+static int          refine_level = 5;
+#else
+static int          refine_level = 3;
+#endif
+
+#ifndef P4_TO_P8
+static p4est_connectivity_t *
+p4est_connectivity_new_lnodes_test (void)
+{
+  const p4est_topidx_t num_vertices = 6;
+  const p4est_topidx_t num_trees = 2;
+  const p4est_topidx_t num_ctt = 0;
+  const double        vertices[6 * 3] = {
+    0, 0, 0,
+    1, 0, 0,
+    0, 1, 0,
+    1, 1, 0,
+    0, 2, 0,
+    1, 2, 0,
+  };
+  const p4est_topidx_t tree_to_vertex[2 * 4] = {
+    2, 3, 4, 5, 0, 1, 2, 3,
+  };
+  const p4est_topidx_t tree_to_tree[2 * 4] = {
+    0, 0, 1, 0, 1, 1, 1, 0,
+  };
+  const int8_t        tree_to_face[2 * 4] = {
+    0, 1, 3, 3, 0, 1, 2, 2,
+  };
+
+  return p4est_connectivity_new_copy (num_vertices, num_trees, 0,
+                                      vertices, tree_to_vertex,
+                                      tree_to_tree, tree_to_face,
+                                      NULL, &num_ctt, NULL, NULL);
+}
+#endif
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  if (which_tree == 2 || which_tree == 3) {
+    return 0;
+  }
+
+  cid = p4est_quadrant_child_id (quadrant);
+
+  if (cid == P4EST_CHILDREN - 1 ||
+      (quadrant->x >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2) &&
+       quadrant->y >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#ifdef P4_TO_P8
+       && quadrant->z >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#endif
+      )) {
+    return 1;
+  }
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && cid == 2) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_QUADRANT_LEN (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->y >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+#ifndef P4_TO_P8
+static int
+refine_fn_lnodes_test (p4est_t * p4est, p4est_topidx_t which_tree,
+                       p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  cid = p4est_quadrant_child_id (quadrant);
+
+  if (!which_tree && cid == 1) {
+    return 1;
+  }
+  if (which_tree == 1 && cid == 2) {
+    return 1;
+  }
+  return 0;
+}
+#endif
+
+typedef struct tpoint
+{
+  p4est_topidx_t      tree;
+  double              point[P4EST_DIM];
+}
+tpoint_t;
+
+static void
+get_point (double point[P4EST_DIM], p4est_quadrant_t * q, int i, int j,
+#ifdef P4_TO_P8
+           int k,
+#endif
+           int degree)
+{
+  p4est_qcoord_t      len = P4EST_QUADRANT_LEN (q->level);
+  double              rlen = (double) P4EST_ROOT_LEN;
+  double              deg = (double) degree;
+  double              qlen = ((double) len) / rlen;
+
+  P4EST_ASSERT (0 <= i && i < degree + 1);
+  P4EST_ASSERT (0 <= j && j < degree + 1);
+#ifdef P4_TO_P8
+  P4EST_ASSERT (0 <= k && k < degree + 1);
+#endif
+
+  point[0] = ((double) q->x) / rlen + (((double) i) / deg) * qlen;
+  point[1] = ((double) q->y) / rlen + (((double) j) / deg) * qlen;
+#ifdef P4_TO_P8
+  point[2] = ((double) q->z) / rlen + (((double) k) / deg) * qlen;
+#endif
+}
+
+static int
+same_point (tpoint_t * a, tpoint_t * b, p4est_connectivity_t * conn)
+{
+  double              tol = 1e-10;
+  int                 a_count = 0;
+  int                 b_count = 0;
+  int                 a_x_pos = -1;
+  int                 b_x_pos = -1;
+  int                 a_y_pos = -1;
+  int                 b_y_pos = -1;
+#ifdef P4_TO_P8
+  int                 a_z_pos = -1;
+  int                 b_z_pos = -1;
+#endif
+  p4est_topidx_t      a_t = a->tree;
+  p4est_topidx_t      b_t = b->tree;
+  int                 ftrans[P4EST_FTRANSFORM];
+  double              a_trans[P4EST_DIM];
+  int                 a_f, b_f;
+  int                 a_nf, b_nf;
+  int                 a_o;
+  int                 a_c, b_c;
+  int                 i;
+  int                 a_c_pos, b_c_pos;
+  p4est_topidx_t      corner;
+  p4est_topidx_t      tz;
+  p4est_topidx_t      a_nt;
+#ifdef P4_TO_P8
+  int                 a_ref, a_set;
+  int                 a_e, b_e;
+  p4est_topidx_t      edge;
+  int                 a_ne;
+  double              a_edge_pos, b_edge_pos;
+  int                 b_o;
+  int                 a_e_c0, a_e_c1;
+  int                 b_e_c0, b_e_c1;
+#endif
+
+  if (a_t == b_t
+      && fabs (a->point[0] - b->point[0]) < tol
+      && fabs (a->point[1] - b->point[1]) < tol
+#ifdef P4_TO_P8
+      && fabs (a->point[2] - b->point[2]) < tol
+#endif
+    ) {
+    return 1;
+  }
+
+  if (fabs (a->point[0]) < tol) {
+    a_x_pos = 0;
+    a_count++;
+  }
+  else if (fabs (a->point[0] - 1.) < tol) {
+    a_x_pos = 1;
+    a_count++;
+  }
+  if (fabs (b->point[0]) < tol) {
+    b_x_pos = 0;
+    b_count++;
+  }
+  else if (fabs (b->point[0] - 1.) < tol) {
+    b_x_pos = 1;
+    b_count++;
+  }
+
+  if (fabs (a->point[1]) < tol) {
+    a_y_pos = 0;
+    a_count++;
+  }
+  else if (fabs (a->point[1] - 1.) < tol) {
+    a_y_pos = 1;
+    a_count++;
+  }
+  if (fabs (b->point[1]) < tol) {
+    b_y_pos = 0;
+    b_count++;
+  }
+  else if (fabs (b->point[1] - 1.) < tol) {
+    b_y_pos = 1;
+    b_count++;
+  }
+
+#ifdef P4_TO_P8
+  if (fabs (a->point[2]) < tol) {
+    a_z_pos = 0;
+    a_count++;
+  }
+  else if (fabs (a->point[2] - 1.) < tol) {
+    a_z_pos = 1;
+    a_count++;
+  }
+  if (fabs (b->point[2]) < tol) {
+    b_z_pos = 0;
+    b_count++;
+  }
+  else if (fabs (b->point[2] - 1.) < tol) {
+    b_z_pos = 1;
+    b_count++;
+  }
+#endif
+
+  if (a_count != b_count) {
+    return 0;
+  }
+
+  switch (a_count) {
+  case 0:
+    return 0;
+    break;
+  case 1:
+    a_f = -1;
+    a_f = a_x_pos >= 0 ? a_x_pos : a_y_pos >= 0 ? a_y_pos + 2 :
+#ifdef P4_TO_P8
+      a_z_pos >= 0 ? a_z_pos + 4 :
+#endif
+      -1;
+    P4EST_ASSERT (a_f >= 0 && a_f < P4EST_FACES);
+
+    b_f = -1;
+    b_f = b_x_pos >= 0 ? b_x_pos : b_y_pos >= 0 ? b_y_pos + 2 :
+#ifdef P4_TO_P8
+      b_z_pos >= 0 ? b_z_pos + 4 :
+#endif
+      -1;
+    P4EST_ASSERT (b_f >= 0 && b_f < P4EST_FACES);
+
+    if (conn->tree_to_tree[a_t * P4EST_FACES + a_f] != b_t ||
+        conn->tree_to_tree[b_t * P4EST_FACES + b_f] != a_t) {
+      return 0;
+    }
+
+    a_nf = (int) conn->tree_to_face[a_t * P4EST_FACES + a_f];
+    a_o = a_nf / P4EST_FACES;
+    a_nf %= P4EST_FACES;
+
+    b_nf = (int) conn->tree_to_face[b_t * P4EST_FACES + b_f];
+    b_nf %= P4EST_FACES;
+
+    if (a_nf != b_f || b_nf != a_f) {
+      return 0;
+    }
+
+    (void) p4est_find_face_transform (conn, a_t, a_f, ftrans);
+
+    a_trans[ftrans[3]] = !ftrans[6] ? a->point[ftrans[0]] :
+      1. - a->point[ftrans[0]];
+#ifdef P4_TO_P8
+    a_trans[ftrans[4]] = !ftrans[7] ? a->point[ftrans[1]] :
+      1. - a->point[ftrans[1]];
+#endif
+    switch (ftrans[8]) {
+    case 0:
+      a_trans[ftrans[5]] = -a->point[ftrans[2]];
+      break;
+    case 1:
+      a_trans[ftrans[5]] = 1. + a->point[ftrans[2]];
+      break;
+    case 2:
+      a_trans[ftrans[5]] = a->point[ftrans[2]] - 1.;
+      break;
+    case 3:
+      a_trans[ftrans[5]] = 2. - a->point[ftrans[2]];
+      break;
+    default:
+      SC_ABORT_NOT_REACHED ();
+    }
+
+    if (fabs (a_trans[0] - b->point[0]) < tol
+        && fabs (a_trans[1] - b->point[1]) < tol
+#ifdef P4_TO_P8
+        && fabs (a_trans[2] - b->point[2]) < tol
+#endif
+      ) {
+      return 1;
+    }
+    else {
+      return 0;
+    }
+  case P4EST_DIM:
+    a_c = a_x_pos + 2 * a_y_pos;
+    b_c = b_x_pos + 2 * b_y_pos;
+#ifdef P4_TO_P8
+    a_c += 4 * a_z_pos;
+    b_c += 4 * b_z_pos;
+#endif
+    for (i = 0; i < P4EST_DIM; i++) {
+      a_f = p4est_corner_faces[a_c][i];
+      if (conn->tree_to_tree[a_t * P4EST_FACES + a_f] == b_t) {
+        a_nf = (int) conn->tree_to_face[a_t * P4EST_FACES + a_f];
+        a_o = a_nf / P4EST_FACES;
+        a_nf %= P4EST_FACES;
+        a_c_pos = p4est_corner_face_corners[a_c][a_f];
+#ifndef P4_TO_P8
+        b_c_pos = !a_o ? a_c_pos : 1 - a_c_pos;
+#else
+        a_ref = p8est_face_permutation_refs[a_f][a_nf];
+        a_set = p8est_face_permutation_sets[a_ref][a_o];
+        b_c_pos = p8est_face_permutations[a_set][a_c_pos];
+#endif
+        if (p4est_face_corners[a_nf][b_c_pos] == b_c) {
+          return 1;
+        }
+      }
+    }
+#ifdef P4_TO_P8
+    if (conn->tree_to_edge != NULL) {
+      for (i = 0; i < 3; i++) {
+        a_e = p8est_corner_edges[a_c][i];
+        edge = conn->tree_to_edge[a_t * 12 + a_e];
+        if (edge != -1) {
+          a_o = -1;
+          a_c_pos = (a_c >> i) & 1;
+          for (tz = conn->ett_offset[edge]; tz < conn->ett_offset[edge + 1];
+               tz++) {
+            a_nt = conn->edge_to_tree[tz];
+            a_ne = (int) conn->edge_to_edge[tz];
+            if (a_nt == a_t && a_ne % 12 == a_e) {
+              a_o = a_ne / 12;
+            }
+          }
+          P4EST_ASSERT (a_o >= 0);
+          for (tz = conn->ett_offset[edge]; tz < conn->ett_offset[edge + 1];
+               tz++) {
+            a_nt = conn->edge_to_tree[tz];
+            if (a_nt != b_t) {
+              continue;
+            }
+            b_e = (int) conn->edge_to_edge[tz];
+            b_o = b_e / 12;
+            b_e %= 12;
+            if (b_o == a_o) {
+              if (p8est_edge_corners[b_e][a_c_pos] == b_c) {
+                return 1;
+              }
+            }
+            else {
+              if (p8est_edge_corners[b_e][1 - a_c_pos] == b_c) {
+                return 1;
+              }
+            }
+          }
+        }
+      }
+    }
+#endif
+    if (conn->tree_to_corner == NULL) {
+      return 0;
+    }
+    corner = conn->tree_to_corner[a_t * P4EST_CHILDREN + a_c];
+    if (corner == -1) {
+      return 0;
+    }
+    for (tz = conn->ctt_offset[corner]; tz < conn->ctt_offset[corner + 1];
+         tz++) {
+      a_nt = conn->corner_to_tree[tz];
+      if (a_nt == b_t && (int) conn->corner_to_corner[tz] == b_c) {
+        return 1;
+      }
+    }
+    return 0;
+#ifdef P4_TO_P8
+  case 2:
+    a_e = 0;
+    if (a_z_pos >= 0) {
+      a_e += a_z_pos;
+    }
+    if (a_y_pos >= 0) {
+      a_e <<= 1;
+      a_e += a_y_pos;
+    }
+    if (a_x_pos >= 0) {
+      a_e <<= 1;
+      a_e += a_x_pos;
+    }
+    P4EST_ASSERT (0 <= a_e && a_e < 4);
+    if (a_z_pos < 0) {
+      a_e += 8;
+      a_edge_pos = a->point[2];
+    }
+    else if (a_y_pos < 0) {
+      a_e += 4;
+      a_edge_pos = a->point[1];
+    }
+    else {
+      P4EST_ASSERT (a_x_pos < 0);
+      a_edge_pos = a->point[0];
+    }
+    b_e = 0;
+    if (b_z_pos >= 0) {
+      b_e += b_z_pos;
+    }
+    if (b_y_pos >= 0) {
+      b_e <<= 1;
+      b_e += b_y_pos;
+    }
+    if (b_x_pos >= 0) {
+      b_e <<= 1;
+      b_e += b_x_pos;
+    }
+    P4EST_ASSERT (0 <= b_e && b_e < 4);
+    if (b_z_pos < 0) {
+      b_e += 8;
+      b_edge_pos = b->point[2];
+    }
+    else if (b_y_pos < 0) {
+      b_e += 4;
+      b_edge_pos = b->point[1];
+    }
+    else {
+      P4EST_ASSERT (b_x_pos < 0);
+      b_edge_pos = b->point[0];
+    }
+    for (i = 0; i < 2; i++) {
+      a_f = p8est_edge_faces[a_e][i];
+      a_nt = conn->tree_to_tree[a_t * P4EST_FACES + a_f];
+      if (a_nt != b_t) {
+        continue;
+      }
+      a_nf = (int) conn->tree_to_face[a_t * P4EST_FACES + a_f];
+      a_o = a_nf / P4EST_FACES;
+      a_nf %= P4EST_FACES;
+      if (p8est_edge_faces[b_e][0] != a_nf &&
+          p8est_edge_faces[b_e][1] != a_nf) {
+        continue;
+      }
+      a_e_c0 = p8est_edge_corners[a_e][0];
+      a_e_c1 = p8est_edge_corners[a_e][1];
+
+      a_ref = p8est_face_permutation_refs[a_f][a_nf];
+      a_set = p8est_face_permutation_sets[a_ref][a_o];
+
+      a_c_pos = p8est_corner_face_corners[a_e_c0][a_f];
+      b_c_pos = p8est_face_permutations[a_set][a_c_pos];
+      b_e_c0 = p8est_face_corners[a_nf][b_c_pos];
+
+      if (p8est_edge_corners[b_e][0] != b_e_c0 &&
+          p8est_edge_corners[b_e][1] != b_e_c0) {
+        continue;
+      }
+
+      a_c_pos = p8est_corner_face_corners[a_e_c1][a_f];
+      b_c_pos = p8est_face_permutations[a_set][a_c_pos];
+      b_e_c1 = p8est_face_corners[a_nf][b_c_pos];
+
+      if (p8est_edge_corners[b_e][0] != b_e_c1 &&
+          p8est_edge_corners[b_e][1] != b_e_c1) {
+        continue;
+      }
+
+      if (p8est_edge_corners[b_e][0] == b_e_c0) {
+        if (fabs (a_edge_pos - b_edge_pos) < tol) {
+          return 1;
+        }
+        else {
+          return 0;
+        }
+      }
+      else {
+        if (fabs (a_edge_pos - (1. - b_edge_pos)) < tol) {
+          return 1;
+        }
+        else {
+          return 0;
+        }
+      }
+    }
+    if (conn->tree_to_edge == NULL) {
+      return 0;
+    }
+    edge = conn->tree_to_edge[a_t * 12 + a_e];
+    if (edge == -1) {
+      return 0;
+    }
+    a_o = -1;
+    for (tz = conn->ett_offset[edge]; tz < conn->ett_offset[edge + 1]; tz++) {
+      a_nt = conn->edge_to_tree[tz];
+      a_ne = (int) conn->edge_to_edge[tz];
+      if (a_nt == a_t && a_ne % 12 == a_e) {
+        a_o = a_ne / 12;
+      }
+    }
+    P4EST_ASSERT (a_o != -1);
+    for (tz = conn->ett_offset[edge]; tz < conn->ett_offset[edge + 1]; tz++) {
+      a_nt = conn->edge_to_tree[tz];
+      a_ne = (int) conn->edge_to_edge[tz];
+      b_o = a_ne / 12;
+      a_ne %= 12;
+      if (a_ne == b_e) {
+        if (a_o == b_o) {
+          if (fabs (a_edge_pos - b_edge_pos) < tol) {
+            return 1;
+          }
+          else {
+            return 0;
+          }
+        }
+        else {
+          if (fabs (a_edge_pos - (1. - b_edge_pos)) < tol) {
+            return 1;
+          }
+          else {
+            return 0;
+          }
+        }
+      }
+    }
+    return 0;
+#endif
+  default:
+    SC_ABORT_NOT_REACHED ();
+    return -1;
+  }
+
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 mpisize, mpirank;
+  p4est_t            *p4est;
+  p4est_connectivity_t *conn;
+  p4est_ghost_t      *ghost_layer;
+  p4est_ghost_t      *face_ghost_layer;
+#ifdef P4_TO_P8
+  p4est_ghost_t      *edge_ghost_layer;
+#endif
+  int                 ntests;
+  int                 i, j, k;
+  p4est_lnodes_t     *lnodes;
+  p4est_locidx_t      nin;
+  tpoint_t           *tpoints, tpoint, *tpoint_p;
+  p4est_locidx_t      elid;
+  p4est_locidx_t      elnid;
+  p4est_locidx_t      nid;
+  p4est_topidx_t      t, flt, llt;
+  p4est_tree_t       *tree;
+  size_t              zz, zy, count;
+  p4est_lnodes_code_t fcode;
+  int                 hface[P4EST_FACES];
+  p4est_quadrant_t   *q, p, *q_ptr;
+  int                 bcount;
+  int                 iind, jind;
+  int                 ib, jb;
+  int                 is_hanging;
+  int                 f;
+  int                 c;
+  sc_array_t          tpoint_array;
+  p4est_lnodes_buffer_t *buffer;
+  sc_array_t         *peer_buffer;
+  p4est_lnodes_rank_t *lrank;
+  sc_array_t         *shared_nodes;
+#ifdef P4_TO_P8
+  int                 hedge[12];
+  int                 kind;
+  int                 kb;
+  int                 e;
+#endif
+  sc_array_t         *global_nodes;
+  p4est_gloidx_t      gn;
+
+#ifndef P4_TO_P8
+  ntests = 4;
+#else
+  ntests = 4;
+#endif
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  for (i = 0; i < ntests; i++) {
+    /* create connectivity and forest structures */
+    switch (i) {
+#ifndef P4_TO_P8
+    case 0:
+      conn = p4est_connectivity_new_moebius ();
+      break;
+    case 1:
+      conn = p4est_connectivity_new_star ();
+      break;
+    case 2:
+      conn = p4est_connectivity_new_periodic ();
+      break;
+    case 3:
+      conn = p4est_connectivity_new_lnodes_test ();
+      break;
+#else
+    case 0:
+      conn = p8est_connectivity_new_periodic ();
+      break;
+    case 1:
+      conn = p8est_connectivity_new_rotwrap ();
+      break;
+    case 2:
+      conn = p8est_connectivity_new_rotcubes ();
+      break;
+    case 3:
+      conn = p8est_connectivity_new_shell ();
+      break;
+#endif
+    default:
+      SC_ABORT_NOT_REACHED ();
+      break;
+    }
+#ifndef P4_TO_P8
+    if (i == 3) {
+      p4est = p4est_new_ext (mpicomm, conn, 0, 1, 1, 0, NULL, NULL);
+    }
+    else {
+      p4est = p4est_new_ext (mpicomm, conn, 15, 0, 0, 0, NULL, NULL);
+    }
+#else
+    p4est = p4est_new_ext (mpicomm, conn, 15, 0, 0, 0, NULL, NULL);
+#endif
+
+    /* refine to make the number of elements interesting */
+#ifndef P4_TO_P8
+    if (i == 3) {
+      p4est_refine (p4est, 0, refine_fn_lnodes_test, NULL);
+      P4EST_ASSERT (p4est_is_balanced (p4est, P4EST_CONNECT_FULL));
+    }
+    else {
+      p4est_refine (p4est, 1, refine_fn, NULL);
+    }
+#else
+    p4est_refine (p4est, 1, refine_fn, NULL);
+#endif
+
+    /* balance the forest */
+#ifndef P4_TO_P8
+    p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+#else
+    p4est_balance (p4est, P8EST_CONNECT_FULL, NULL);
+#endif
+
+    /* do a uniform partition */
+#ifndef P4_TO_P8
+    if (i == 3 && mpisize == 3) {
+      p4est_locidx_t      num_quads[3] = { 3, 8, 3 };
+      p4est_partition_given (p4est, num_quads);
+    }
+    else {
+      p4est_partition (p4est, 0, NULL);
+    }
+#else
+    p4est_partition (p4est, 0, NULL);
+#endif
+
+    ghost_layer = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+    face_ghost_layer = p4est_ghost_new (p4est, P4EST_CONNECT_FACE);
+#ifdef P4_TO_P8
+    edge_ghost_layer = p4est_ghost_new (p4est, P8EST_CONNECT_EDGE);
+#endif
+
+    flt = p4est->first_local_tree;
+    llt = p4est->last_local_tree;
+
+    for (j = -P4EST_DIM; j <= 4; j++) {
+      if (!j) {
+        continue;
+      }
+      P4EST_GLOBAL_PRODUCTIONF ("Begin lnodes test %d:%d\n", i, j);
+      p4est_log_indent_push ();
+      switch (j) {
+#ifdef P4_TO_P8
+      case -2:
+        lnodes = p4est_lnodes_new (p4est, edge_ghost_layer, j);
+        break;
+#endif
+      case -1:
+        lnodes = p4est_lnodes_new (p4est, face_ghost_layer, j);
+        break;
+      default:
+        lnodes = p4est_lnodes_new (p4est, ghost_layer, j);
+        break;
+      }
+
+      if (j < 0) {
+        p4est_lnodes_destroy (lnodes);
+        p4est_log_indent_pop ();
+        continue;
+      }
+      nin = lnodes->num_local_nodes;
+      tpoints = P4EST_ALLOC (tpoint_t, nin);
+      memset (tpoints, -1, nin * sizeof (tpoint_t));
+      for (elid = 0, elnid = 0, t = flt; t <= llt; t++) {
+        tree = p4est_tree_array_index (p4est->trees, t);
+        count = tree->quadrants.elem_count;
+        tpoint.tree = t;
+        /* for every node of every element,
+         * determine what kind of node it is */
+        for (zz = 0; zz < count; zz++, elid++) {
+          fcode = lnodes->face_code[elid];
+          p4est_lnodes_decode (fcode, hface
+#ifdef P4_TO_P8
+                               , hedge
+#endif
+            );
+          q = p4est_quadrant_array_index (&tree->quadrants, zz);
+          p4est_quadrant_parent (q, &p);
+#ifdef P4_TO_P8
+          for (kind = 0; kind < j + 1; kind++) {
+#endif
+            for (jind = 0; jind < j + 1; jind++) {
+              for (iind = 0; iind < j + 1; iind++) {
+                bcount = 0;
+                if (iind == 0) {
+                  ib = 0;
+                  bcount++;
+                }
+                else if (iind == j) {
+                  ib = 1;
+                  bcount++;
+                }
+                else {
+                  ib = -1;
+                }
+                if (jind == 0) {
+                  jb = 0;
+                  bcount++;
+                }
+                else if (jind == j) {
+                  jb = 1;
+                  bcount++;
+                }
+                else {
+                  jb = -1;
+                }
+#ifdef P4_TO_P8
+                if (kind == 0) {
+                  kb = 0;
+                  bcount++;
+                }
+                else if (kind == j) {
+                  kb = 1;
+                  bcount++;
+                }
+                else {
+                  kb = -1;
+                }
+#endif
+                is_hanging = 0;
+                /* if it touches a hanging part of the boundary, then the
+                 * location of the element node is really the location of a
+                 * node with the same index in the element's parent */
+                if (fcode) {
+                  switch (bcount) {
+                  case 0:
+                    break;
+                  case 1:
+                    f = (ib >= 0) ? ib : (jb >= 0) ? 2 + jb
+#ifdef P4_TO_P8
+                      : (kb >= 0) ? 4 + kb
+#endif
+                      : -1;
+                    P4EST_ASSERT (f >= 0);
+                    if (fcode && hface[f] >= 0) {
+                      is_hanging = 1;
+                    }
+                    break;
+                  case P4EST_DIM:
+                    c = ib + 2 * jb
+#ifdef P4_TO_P8
+                      + 4 * kb
+#endif
+                      ;
+                    for (k = 0; k < P4EST_DIM; k++) {
+                      f = p4est_corner_faces[c][k];
+                      if (fcode && hface[f] >= 0) {
+                        is_hanging = 1;
+                      }
+                    }
+#ifdef P4_TO_P8
+                    for (k = 0; k < 3; k++) {
+                      e = p8est_corner_edges[c][k];
+                      if (fcode && hedge[e] >= 0) {
+                        is_hanging = 1;
+                      }
+                    }
+#endif
+                    break;
+#ifdef P4_TO_P8
+                  case 2:
+                    e = 0;
+                    if (kb >= 0) {
+                      e += kb;
+                    }
+                    if (jb >= 0) {
+                      e <<= 1;
+                      e += jb;
+                    }
+                    if (ib >= 0) {
+                      e <<= 1;
+                      e += ib;
+                    }
+                    P4EST_ASSERT (0 <= e && e < 4);
+                    if (kb < 0) {
+                      e = e + 8;
+                    }
+                    else if (jb < 0) {
+                      e = e + 4;
+                    }
+#ifdef P4EST_ENABLE_DEBUG
+                    else {
+                      P4EST_ASSERT (ib < 0);
+                    }
+#endif
+                    if (fcode && hedge[e] >= 0) {
+                      is_hanging = 1;
+                    }
+                    break;
+#endif
+                  default:
+                    SC_ABORT_NOT_REACHED ();
+                  }
+                }
+                q_ptr = (!is_hanging) ? q : &p;
+                get_point (tpoint.point, q_ptr, iind, jind,
+#ifdef P4_TO_P8
+                           kind,
+#endif
+                           j);
+                nid = lnodes->element_nodes[elnid];
+                if (tpoints[nid].tree == -1) {
+                  tpoints[nid].tree = t;
+                  tpoints[nid].point[0] = tpoint.point[0];
+                  tpoints[nid].point[1] = tpoint.point[1];
+#ifdef P4_TO_P8
+                  tpoints[nid].point[2] = tpoint.point[2];
+#endif
+                }
+                else {
+                  SC_CHECK_ABORT (same_point (&tpoint, tpoints + nid, conn),
+                                  "Lnodes: bad element-to-global node map");
+                }
+                elnid++;
+              }
+            }
+#ifdef P4_TO_P8
+          }
+#endif
+        }
+      }
+
+      sc_array_init_data (&tpoint_array, tpoints, sizeof (tpoint_t), nin);
+      buffer = p4est_lnodes_share_all (&tpoint_array, lnodes);
+
+      for (zz = 0; zz < lnodes->sharers->elem_count; zz++) {
+        lrank = p4est_lnodes_rank_array_index (lnodes->sharers, zz);
+        if (lrank->rank == mpirank) {
+          continue;
+        }
+        peer_buffer =
+          (sc_array_t *) sc_array_index (buffer->recv_buffers, zz);
+        shared_nodes = &(lrank->shared_nodes);
+        P4EST_ASSERT (shared_nodes->elem_count == peer_buffer->elem_count);
+        for (zy = 0; zy < shared_nodes->elem_count; zy++) {
+          nid = *((p4est_locidx_t *) sc_array_index (shared_nodes, zy));
+          tpoint_p = (tpoint_t *) sc_array_index (peer_buffer, zy);
+          SC_CHECK_ABORT (same_point (tpoint_p, tpoints + nid, conn),
+                          "Lnodes: bad element-to-global node map across processors");
+        }
+      }
+
+      p4est_lnodes_buffer_destroy (buffer);
+
+      global_nodes = sc_array_new (sizeof (p4est_gloidx_t));
+      sc_array_resize (global_nodes, lnodes->num_local_nodes);
+      for (zz = 0; zz < global_nodes->elem_count; zz++) {
+        *((p4est_gloidx_t *) sc_array_index (global_nodes, zz)) =
+          p4est_lnodes_global_index (lnodes, zz);
+      }
+
+      p4est_lnodes_share_owned (global_nodes, lnodes);
+
+      for (zz = 0; zz < global_nodes->elem_count; zz++) {
+        gn = *((p4est_gloidx_t *) sc_array_index (global_nodes, zz));
+        SC_CHECK_ABORT (gn == p4est_lnodes_global_index (lnodes, zz),
+                        "Lnodes: bad global index across procesors");
+      }
+
+      sc_array_destroy (global_nodes);
+
+      p4est_lnodes_destroy (lnodes);
+      P4EST_FREE (tpoints);
+      p4est_log_indent_pop ();
+      P4EST_GLOBAL_PRODUCTIONF ("End lnodes test %d:%d\n", i, j);
+    }
+
+    /* clean up */
+    p4est_ghost_destroy (ghost_layer);
+    p4est_ghost_destroy (face_ghost_layer);
+#ifdef P4_TO_P8
+    p4est_ghost_destroy (edge_ghost_layer);
+#endif
+
+    p4est_destroy (p4est);
+    p4est_connectivity_destroy (conn);
+  }
+
+  /* exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
+
+/* EOF test_lnodes2.c */
diff --git a/test/test_lnodes3.c b/test/test_lnodes3.c
new file mode 100644
index 0000000..7643b8e
--- /dev/null
+++ b/test/test_lnodes3.c
@@ -0,0 +1,27 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_lnodes2.c"
+
+/* EOF test_lnodes3.c */
diff --git a/test/test_loadsave2.c b/test/test_loadsave2.c
new file mode 100644
index 0000000..15983ab
--- /dev/null
+++ b/test/test_loadsave2.c
@@ -0,0 +1,350 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_communication.h>
+#include <p4est_extended.h>
+#include <p4est_io.h>
+#else
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_communication.h>
+#include <p8est_extended.h>
+#include <p8est_io.h>
+#endif
+#include <sc_options.h>
+#include <sc_statistics.h>
+
+#ifndef P4_TO_P8
+#define P4EST_CONN_SUFFIX "p4c"
+#define P4EST_FOREST_SUFFIX "p4p"
+static const int    default_refine_level = 7;
+#else
+#define P4EST_CONN_SUFFIX "p8c"
+#define P4EST_FOREST_SUFFIX "p8p"
+static const int    default_refine_level = 4;
+#endif
+static int          refine_level = 0;
+static int          counter = 0;
+
+static void
+init_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+         p4est_quadrant_t * quadrant)
+{
+  int                *data = (int *) quadrant->p.user_data;
+
+  *data = (counter = counter * 1664525 + 1013904223) + (int) which_tree;
+}
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  if (which_tree == 2 || which_tree == 3) {
+    return 0;
+  }
+
+  cid = p4est_quadrant_child_id (quadrant);
+
+  if (cid == P4EST_CHILDREN - 1 ||
+      (quadrant->x >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2) &&
+       quadrant->y >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#ifdef P4_TO_P8
+       && quadrant->z >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#endif
+      )) {
+    return 1;
+  }
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && cid == 2) {
+    return 1;
+  }
+  if (quadrant->y == P4EST_QUADRANT_LEN (2) &&
+      quadrant->x == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->y >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+enum
+{
+  STATS_CONN_LOAD,
+  STATS_P4EST_SAVE1,
+  STATS_P4EST_LOAD1a,
+  STATS_P4EST_LOAD1b,
+  STATS_P4EST_ELEMS,
+  STATS_P4EST_SAVE2,
+  STATS_P4EST_LOAD2,
+  STATS_P4EST_SAVE3,
+  STATS_P4EST_LOAD3,
+  STATS_P4EST_LOAD4,
+  STATS_COUNT
+};
+
+static void
+test_deflate (p4est_t * p4est)
+{
+  p4est_gloidx_t     *pertree;
+  p4est_t            *p4est2;
+  sc_array_t         *qarr, *darr;
+
+  pertree = P4EST_ALLOC (p4est_gloidx_t, p4est->connectivity->num_trees + 1);
+  p4est_comm_count_pertree (p4est, pertree);
+  darr = NULL;
+  qarr = p4est_deflate_quadrants (p4est, p4est->data_size > 0 ? &darr : NULL);
+
+  /* Data that describes the forest completely
+     (a) shared data (identical on all processors):
+     p4est->connectivity
+     p4est->global_first_quadrant (does not need to be stored away)
+     pertree
+     (b) per-processor data (partition independent after allgatherv):
+     qarr
+     darr (if per-quadrant data size is greater 0 and it should be saved)
+   */
+
+  /* Create a forest from this information and compare */
+  p4est2 = p4est_inflate (p4est->mpicomm, p4est->connectivity,
+                          p4est->global_first_quadrant, pertree,
+                          qarr, darr, p4est->user_pointer);
+  SC_CHECK_ABORT (p4est_is_equal (p4est, p4est2, 1), "de/inflate");
+  p4est_destroy (p4est2);
+
+  /* clean up allocated memory */
+  P4EST_FREE (pertree);
+  sc_array_destroy (qarr);
+  if (darr != NULL) {
+    sc_array_destroy (darr);
+  }
+}
+
+static void
+test_loadsave (p4est_connectivity_t * connectivity, const char *prefix,
+               sc_MPI_Comm mpicomm, int mpirank)
+{
+  int                 mpiret, retval;
+  unsigned            csum, csum2;
+  double              elapsed, wtime;
+  p4est_connectivity_t *conn2;
+  p4est_t            *p4est, *p4est2;
+  sc_statinfo_t       stats[STATS_COUNT];
+  char                conn_name[BUFSIZ];
+  char                p4est_name[BUFSIZ];
+
+  snprintf (conn_name, BUFSIZ, "%s.%s", prefix, P4EST_CONN_SUFFIX);
+  snprintf (p4est_name, BUFSIZ, "%s.%s", prefix, P4EST_FOREST_SUFFIX);
+  P4EST_GLOBAL_INFOF ("Using file names %s and %s\n", conn_name, p4est_name);
+
+  p4est = p4est_new_ext (mpicomm, connectivity, 0, 0, 0,
+                         sizeof (int), init_fn, NULL);
+  p4est_refine (p4est, 1, refine_fn, init_fn);
+  test_deflate (p4est);
+
+  /* save, synchronize, load connectivity and compare */
+  if (mpirank == 0) {
+    retval = p4est_connectivity_save (conn_name, connectivity);
+    SC_CHECK_ABORT (retval == 0, "connectivity_save failed");
+  }
+  mpiret = sc_MPI_Barrier (mpicomm);
+  SC_CHECK_MPI (mpiret);
+
+  wtime = sc_MPI_Wtime ();
+  conn2 = p4est_connectivity_load (conn_name, NULL);
+  elapsed = sc_MPI_Wtime () - wtime;
+  sc_stats_set1 (stats + STATS_CONN_LOAD, elapsed, "conn load");
+
+  SC_CHECK_ABORT (p4est_connectivity_is_equal (connectivity, conn2),
+                  "load/save connectivity mismatch A");
+  p4est_connectivity_destroy (conn2);
+
+  /* save, synchronize, load p4est and compare */
+  wtime = sc_MPI_Wtime ();
+  p4est_save (p4est_name, p4est, 1);
+  elapsed = sc_MPI_Wtime () - wtime;
+  sc_stats_set1 (stats + STATS_P4EST_SAVE1, elapsed, "p4est save 1");
+
+  wtime = sc_MPI_Wtime ();
+  p4est2 = p4est_load (p4est_name, mpicomm, sizeof (int), 1, NULL, &conn2);
+  elapsed = sc_MPI_Wtime () - wtime;
+  sc_stats_set1 (stats + STATS_P4EST_LOAD1a, elapsed, "p4est load 1a");
+
+  SC_CHECK_ABORT (p4est_connectivity_is_equal (connectivity, conn2),
+                  "load/save connectivity mismatch Ba");
+  SC_CHECK_ABORT (p4est_is_equal (p4est, p4est2, 1),
+                  "load/save p4est mismatch Ba");
+  p4est_destroy (p4est2);
+  p4est_connectivity_destroy (conn2);
+
+  wtime = sc_MPI_Wtime ();
+  p4est2 = p4est_load (p4est_name, mpicomm, 0, 0, NULL, &conn2);
+  elapsed = sc_MPI_Wtime () - wtime;
+  sc_stats_set1 (stats + STATS_P4EST_LOAD1b, elapsed, "p4est load 1b");
+
+  SC_CHECK_ABORT (p4est_connectivity_is_equal (connectivity, conn2),
+                  "load/save connectivity mismatch Bb");
+  SC_CHECK_ABORT (p4est_is_equal (p4est, p4est2, 0),
+                  "load/save p4est mismatch Bb");
+  test_deflate (p4est2);
+  p4est_destroy (p4est2);
+  p4est_connectivity_destroy (conn2);
+
+  /* partition and balance */
+  p4est_partition (p4est, 0, NULL);
+  p4est_balance (p4est, P4EST_CONNECT_FULL, init_fn);
+  csum = p4est_checksum (p4est);
+  sc_stats_set1 (stats + STATS_P4EST_ELEMS,
+                 (double) p4est->local_num_quadrants, "p4est elements");
+
+  /* save, synchronize, load p4est and compare */
+  wtime = sc_MPI_Wtime ();
+  p4est_save (p4est_name, p4est, 0);
+  elapsed = sc_MPI_Wtime () - wtime;
+  sc_stats_set1 (stats + STATS_P4EST_SAVE2, elapsed, "p4est save 2");
+
+  wtime = sc_MPI_Wtime ();
+  p4est2 = p4est_load (p4est_name, mpicomm, sizeof (int), 0, NULL, &conn2);
+  elapsed = sc_MPI_Wtime () - wtime;
+  sc_stats_set1 (stats + STATS_P4EST_LOAD2, elapsed, "p4est load 2");
+
+  SC_CHECK_ABORT (p4est_connectivity_is_equal (connectivity, conn2),
+                  "load/save connectivity mismatch C");
+  SC_CHECK_ABORT (p4est_is_equal (p4est, p4est2, 0),
+                  "load/save p4est mismatch C");
+  p4est_destroy (p4est2);
+  p4est_connectivity_destroy (conn2);
+
+  /* save, synchronize, load p4est and compare */
+  wtime = sc_MPI_Wtime ();
+  p4est_save (p4est_name, p4est, 1);
+  elapsed = sc_MPI_Wtime () - wtime;
+  sc_stats_set1 (stats + STATS_P4EST_SAVE3, elapsed, "p4est save 3");
+
+  wtime = sc_MPI_Wtime ();
+  p4est2 = p4est_load (p4est_name, mpicomm, sizeof (int), 0, NULL, &conn2);
+  elapsed = sc_MPI_Wtime () - wtime;
+  sc_stats_set1 (stats + STATS_P4EST_LOAD3, elapsed, "p4est load 3");
+
+  SC_CHECK_ABORT (p4est_connectivity_is_equal (connectivity, conn2),
+                  "load/save connectivity mismatch D");
+  SC_CHECK_ABORT (p4est_is_equal (p4est, p4est2, 0),
+                  "load/save p4est mismatch D");
+  p4est_destroy (p4est2);
+  p4est_connectivity_destroy (conn2);
+
+  /* Test autopartition load feature */
+  wtime = sc_MPI_Wtime ();
+  p4est2 = p4est_load_ext (p4est_name, mpicomm, sizeof (int), 0,
+                           1, 0, NULL, &conn2);
+  elapsed = sc_MPI_Wtime () - wtime;
+  csum2 = p4est_checksum (p4est2);
+  sc_stats_set1 (stats + STATS_P4EST_LOAD4, elapsed, "p4est load 4");
+
+  SC_CHECK_ABORT (p4est_connectivity_is_equal (connectivity, conn2),
+                  "load/save connectivity mismatch E");
+  SC_CHECK_ABORT (mpirank != 0 || csum == csum2,
+                  "load/save p4est mismatch E");
+  p4est_destroy (p4est2);
+  p4est_connectivity_destroy (conn2);
+
+  /* destroy data structures */
+  p4est_destroy (p4est);
+
+  /* compute and print timings */
+  sc_stats_compute (mpicomm, STATS_COUNT, stats);
+  sc_stats_print (p4est_package_id, SC_LP_STATISTICS,
+                  STATS_COUNT, stats, 0, 1);
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 mpirank;
+  int                 first_arg;
+  const char         *prefix;
+  p4est_connectivity_t *connectivity;
+  sc_options_t       *opt;
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  /* initialize libsc and p4est */
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* handle command line options */
+  opt = sc_options_new (argv[0]);
+  sc_options_add_int (opt, 'l', "level", &refine_level,
+                      default_refine_level, "Refinement level");
+  sc_options_add_string (opt, 'o', "oprefix", &prefix,
+                         P4EST_STRING, "Output prefix");
+  first_arg = sc_options_parse (p4est_package_id, SC_LP_INFO,
+                                opt, argc, argv);
+  SC_CHECK_ABORT (first_arg >= 0, "Option error");
+
+  /* create connectivity */
+#ifndef P4_TO_P8
+  connectivity = p4est_connectivity_new_star ();
+#else
+  connectivity = p8est_connectivity_new_rotcubes ();
+#endif
+
+  /* test with vertex information */
+  test_loadsave (connectivity, prefix, mpicomm, mpirank);
+
+  /* test without vertex information */
+  connectivity->num_vertices = 0;
+  P4EST_FREE (connectivity->vertices);
+  connectivity->vertices = NULL;
+  P4EST_FREE (connectivity->tree_to_vertex);
+  connectivity->tree_to_vertex = NULL;
+  p4est_connectivity_set_attr (connectivity, 1);
+  memset (connectivity->tree_to_attr, 0,
+          connectivity->num_trees * sizeof (int8_t));
+  test_loadsave (connectivity, prefix, mpicomm, mpirank);
+
+  /* clean up and exit */
+  p4est_connectivity_destroy (connectivity);
+  sc_options_destroy (opt);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_loadsave3.c b/test/test_loadsave3.c
new file mode 100644
index 0000000..7a833e9
--- /dev/null
+++ b/test/test_loadsave3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_loadsave2.c"
diff --git a/test/test_order.c b/test/test_order.c
new file mode 100644
index 0000000..f75df55
--- /dev/null
+++ b/test/test_order.c
@@ -0,0 +1,407 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_nodes.h>
+
+typedef struct
+{
+  p4est_topidx_t      a;
+  int64_t             sum;
+}
+user_data_t;
+
+typedef struct p4est_vert
+{
+  double              x, y, z;
+  p4est_topidx_t      treeid;
+}
+p4est_vert_t;
+
+static int          refine_level = 6;
+
+static void
+init_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+         p4est_quadrant_t * quadrant)
+{
+  user_data_t        *data = (user_data_t *) quadrant->p.user_data;
+
+  data->a = which_tree;
+  data->sum = quadrant->x + quadrant->y + quadrant->level;
+}
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  if (quadrant->level >= (refine_level - (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && p4est_quadrant_child_id (quadrant) == 3) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_LAST_OFFSET (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->x >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int
+weight_one (p4est_t * p4est, p4est_topidx_t which_tree,
+            p4est_quadrant_t * quadrant)
+{
+  return 1;
+}
+
+static int
+p4est_vert_compare (const void *a, const void *b)
+{
+  const p4est_vert_t *v1 = (const p4est_vert_t *) a;
+  const p4est_vert_t *v2 = (const p4est_vert_t *) b;
+  const double        eps = 1e-15;
+  double              xdiff, ydiff, zdiff;
+  int                 retval = 0;
+
+  if (v1->treeid != v2->treeid) {
+    return (int) v1->treeid - (int) v2->treeid;
+  }
+
+  xdiff = fabs (v1->x - v2->x);
+  if (xdiff < eps) {
+    ydiff = fabs (v1->y - v2->y);
+    if (ydiff < eps) {
+      zdiff = fabs (v1->z - v2->z);
+      if (zdiff < eps) {
+        retval = 0;
+      }
+      else {
+        retval = (v1->z < v2->z) ? -1 : 1;
+      }
+    }
+    else {
+      retval = (v1->y < v2->y) ? -1 : 1;
+    }
+  }
+  else {
+    retval = (v1->x < v2->x) ? -1 : 1;
+  }
+
+  return retval;
+}
+
+static void
+p4est_check_local_order (p4est_t * p4est, p4est_connectivity_t * connectivity)
+{
+  const double        intsize = 1.0 / P4EST_ROOT_LEN;
+  double             *vertices;
+  double              h, eta1, eta2;
+  double              v0x, v0y, v0z, v1x, v1y, v1z;
+  double              v2x, v2y, v2z, v3x, v3y, v3z;
+  double              w0x, w0y, w0z, w1x, w1y, w1z;
+  double              w2x, w2y, w2z, w3x, w3y, w3z;
+  size_t              iz;
+  size_t              num_quads;
+  size_t              quad_count;
+  p4est_topidx_t      jt;
+  p4est_topidx_t     *tree_to_vertex;
+  p4est_topidx_t      first_local_tree;
+  p4est_topidx_t      last_local_tree;
+  p4est_topidx_t      v0, v1, v2, v3;
+  p4est_locidx_t      kl;
+  p4est_locidx_t      lv0, lv1, lv2, lv3;
+  p4est_locidx_t      num_uniq_local_vertices;
+  p4est_locidx_t     *quadrant_to_local_vertex;
+  p4est_qcoord_t      inth;
+  p4est_tree_t       *tree;
+  p4est_quadrant_t   *quad;
+  p4est_vert_t       *vert_locations;
+  p4est_nodes_t      *nodes;
+  sc_array_t         *trees;
+  sc_array_t         *quadrants;
+
+  nodes = p4est_nodes_new (p4est, NULL);
+  quadrant_to_local_vertex = nodes->local_nodes;
+  num_uniq_local_vertices = nodes->num_owned_indeps;
+  SC_CHECK_ABORT ((size_t) num_uniq_local_vertices ==
+                  nodes->indep_nodes.elem_count, "Node count mismatch");
+
+  P4EST_INFOF ("Unique local vertices %lld\n",
+               (long long) num_uniq_local_vertices);
+
+  vert_locations = P4EST_ALLOC (p4est_vert_t, num_uniq_local_vertices);
+  for (kl = 0; kl < num_uniq_local_vertices; ++kl) {
+    vert_locations[kl].treeid = -1;
+  }
+
+  tree_to_vertex = connectivity->tree_to_vertex;
+  vertices = connectivity->vertices;
+  first_local_tree = p4est->first_local_tree;
+  last_local_tree = p4est->last_local_tree;
+  trees = p4est->trees;
+
+  for (jt = first_local_tree, quad_count = 0; jt <= last_local_tree; ++jt) {
+    tree = p4est_tree_array_index (trees, jt);
+
+    P4EST_ASSERT (0 <= jt && jt < connectivity->num_trees);
+
+    v0 = tree_to_vertex[jt * 4 + 0];
+    v1 = tree_to_vertex[jt * 4 + 1];
+    v2 = tree_to_vertex[jt * 4 + 2];
+    v3 = tree_to_vertex[jt * 4 + 3];
+
+    P4EST_ASSERT (0 <= v0 && v0 < connectivity->num_vertices);
+    P4EST_ASSERT (0 <= v1 && v1 < connectivity->num_vertices);
+    P4EST_ASSERT (0 <= v2 && v2 < connectivity->num_vertices);
+    P4EST_ASSERT (0 <= v3 && v3 < connectivity->num_vertices);
+
+    v0x = vertices[v0 * 3 + 0];
+    v0y = vertices[v0 * 3 + 1];
+    v0z = vertices[v0 * 3 + 2];
+    v1x = vertices[v1 * 3 + 0];
+    v1y = vertices[v1 * 3 + 1];
+    v1z = vertices[v1 * 3 + 2];
+    v2x = vertices[v2 * 3 + 0];
+    v2y = vertices[v2 * 3 + 1];
+    v2z = vertices[v2 * 3 + 2];
+    v3x = vertices[v3 * 3 + 0];
+    v3y = vertices[v3 * 3 + 1];
+    v3z = vertices[v3 * 3 + 2];
+
+    quadrants = &tree->quadrants;
+    num_quads = quadrants->elem_count;
+
+    /* loop over the elements in the tree */
+    for (iz = 0; iz < num_quads; ++iz, ++quad_count) {
+      quad = p4est_quadrant_array_index (quadrants, iz);
+      inth = P4EST_QUADRANT_LEN (quad->level);
+      h = intsize * inth;
+      eta1 = intsize * quad->x;
+      eta2 = intsize * quad->y;
+
+      w0x = v0x * (1.0 - eta1) * (1.0 - eta2)
+        + v1x * (eta1) * (1.0 - eta2)
+        + v2x * (1.0 - eta1) * (eta2)
+        + v3x * (eta1) * (eta2);
+
+      w0y = v0y * (1.0 - eta1) * (1.0 - eta2)
+        + v1y * (eta1) * (1.0 - eta2)
+        + v2y * (1.0 - eta1) * (eta2)
+        + v3y * (eta1) * (eta2);
+
+      w0z = v0z * (1.0 - eta1) * (1.0 - eta2)
+        + v1z * (eta1) * (1.0 - eta2)
+        + v2z * (1.0 - eta1) * (eta2)
+        + v3z * (eta1) * (eta2);
+
+      w1x = v0x * (1.0 - eta1 - h) * (1.0 - eta2)
+        + v1x * (eta1 + h) * (1.0 - eta2)
+        + v2x * (1.0 - eta1 - h) * (eta2)
+        + v3x * (eta1 + h) * (eta2);
+
+      w1y = v0y * (1.0 - eta1 - h) * (1.0 - eta2)
+        + v1y * (eta1 + h) * (1.0 - eta2)
+        + v2y * (1.0 - eta1 - h) * (eta2)
+        + v3y * (eta1 + h) * (eta2);
+
+      w1z = v0z * (1.0 - eta1 - h) * (1.0 - eta2)
+        + v1z * (eta1 + h) * (1.0 - eta2)
+        + v2z * (1.0 - eta1 - h) * (eta2)
+        + v3z * (eta1 + h) * (eta2);
+
+      w2x = v0x * (1.0 - eta1) * (1.0 - eta2 - h)
+        + v1x * (eta1) * (1.0 - eta2 - h)
+        + v2x * (1.0 - eta1) * (eta2 + h)
+        + v3x * (eta1) * (eta2 + h);
+
+      w2y = v0y * (1.0 - eta1) * (1.0 - eta2 - h)
+        + v1y * (eta1) * (1.0 - eta2 - h)
+        + v2y * (1.0 - eta1) * (eta2 + h)
+        + v3y * (eta1) * (eta2 + h);
+
+      w2z = v0z * (1.0 - eta1) * (1.0 - eta2 - h)
+        + v1z * (eta1) * (1.0 - eta2 - h)
+        + v2z * (1.0 - eta1) * (eta2 + h)
+        + v3z * (eta1) * (eta2 + h);
+
+      w3x = v0x * (1.0 - eta1 - h) * (1.0 - eta2 - h)
+        + v1x * (eta1 + h) * (1.0 - eta2 - h)
+        + v2x * (1.0 - eta1 - h) * (eta2 + h)
+        + v3x * (eta1 + h) * (eta2 + h);
+
+      w3y = v0y * (1.0 - eta1 - h) * (1.0 - eta2 - h)
+        + v1y * (eta1 + h) * (1.0 - eta2 - h)
+        + v2y * (1.0 - eta1 - h) * (eta2 + h)
+        + v3y * (eta1 + h) * (eta2 + h);
+
+      w3z = v0z * (1.0 - eta1 - h) * (1.0 - eta2 - h)
+        + v1z * (eta1 + h) * (1.0 - eta2 - h)
+        + v2z * (1.0 - eta1 - h) * (eta2 + h)
+        + v3z * (eta1 + h) * (eta2 + h);
+
+      P4EST_ASSERT ((p4est_locidx_t) quad_count < p4est->local_num_quadrants);
+
+      lv0 = quadrant_to_local_vertex[4 * quad_count + 0];
+      lv1 = quadrant_to_local_vertex[4 * quad_count + 1];
+      lv2 = quadrant_to_local_vertex[4 * quad_count + 2];
+      lv3 = quadrant_to_local_vertex[4 * quad_count + 3];
+
+      P4EST_ASSERT (0 <= lv0 && lv0 < num_uniq_local_vertices);
+      P4EST_ASSERT (0 <= lv1 && lv1 < num_uniq_local_vertices);
+      P4EST_ASSERT (0 <= lv2 && lv2 < num_uniq_local_vertices);
+      P4EST_ASSERT (0 <= lv3 && lv3 < num_uniq_local_vertices);
+
+      vert_locations[lv0].x = w0x;
+      vert_locations[lv0].y = w0y;
+      vert_locations[lv0].z = w0z;
+      P4EST_ASSERT (vert_locations[lv0].treeid == -1 ||
+                    vert_locations[lv0].treeid == jt);
+      vert_locations[lv0].treeid = jt;
+
+      vert_locations[lv1].x = w1x;
+      vert_locations[lv1].y = w1y;
+      vert_locations[lv1].z = w1z;
+      P4EST_ASSERT (vert_locations[lv1].treeid == -1 ||
+                    vert_locations[lv1].treeid == jt);
+      vert_locations[lv1].treeid = jt;
+
+      vert_locations[lv2].x = w2x;
+      vert_locations[lv2].y = w2y;
+      vert_locations[lv2].z = w2z;
+      P4EST_ASSERT (vert_locations[lv2].treeid == -1 ||
+                    vert_locations[lv2].treeid == jt);
+      vert_locations[lv2].treeid = jt;
+
+      vert_locations[lv3].x = w3x;
+      vert_locations[lv3].y = w3y;
+      vert_locations[lv3].z = w3z;
+      P4EST_ASSERT (vert_locations[lv3].treeid == -1 ||
+                    vert_locations[lv3].treeid == jt);
+      vert_locations[lv3].treeid = jt;
+    }
+  }
+
+  qsort (vert_locations, num_uniq_local_vertices, sizeof (p4est_vert_t),
+         p4est_vert_compare);
+
+  /* Check to make sure that we don't have any duplicates in the list */
+  for (kl = 0; kl < num_uniq_local_vertices - 1; ++kl) {
+    SC_CHECK_ABORT (p4est_vert_compare (vert_locations + kl,
+                                        vert_locations + kl + 1) != 0,
+                    "local ordering not unique");
+  }
+
+  P4EST_FREE (vert_locations);
+  p4est_nodes_destroy (nodes);
+}
+
+static int          weight_counter;
+static int          weight_index;
+
+static int
+weight_once (p4est_t * p4est, p4est_topidx_t which_tree,
+             p4est_quadrant_t * quadrant)
+{
+  if (weight_counter++ == weight_index) {
+    return 1;
+  }
+
+  return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 rank;
+  int                 mpiret;
+  sc_MPI_Comm         mpicomm;
+  p4est_t            *p4est;
+  p4est_connectivity_t *connectivity;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* create connectivity and forest structures */
+  connectivity = p4est_connectivity_new_star ();
+  p4est = p4est_new_ext (mpicomm, connectivity, 15, 0, 0,
+                         sizeof (user_data_t), init_fn, NULL);
+
+  /* refine to make the number of elements interesting */
+  p4est_refine (p4est, 1, refine_fn, init_fn);
+
+  /* balance the forest */
+  p4est_balance (p4est, P4EST_CONNECT_FULL, init_fn);
+
+  /* do a uniform partition, include the weight function for testing */
+  p4est_partition (p4est, 0, weight_one);
+
+  p4est_check_local_order (p4est, connectivity);
+
+  /* do a weighted partition with many zero weights */
+  weight_counter = 0;
+  weight_index = (rank == 1) ? 1342 : 0;
+  p4est_partition (p4est, 0, weight_once);
+
+  p4est_check_local_order (p4est, connectivity);
+
+  /* clean up */
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (connectivity);
+
+  /* create connectivity and forest structures */
+  connectivity = p4est_connectivity_new_periodic ();
+  p4est = p4est_new_ext (mpicomm, connectivity, 15, 0, 0,
+                         sizeof (user_data_t), init_fn, NULL);
+
+  /* refine to make the number of elements interesting */
+  p4est_refine (p4est, 1, refine_fn, init_fn);
+
+  /* balance the forest */
+  p4est_balance (p4est, P4EST_CONNECT_FULL, init_fn);
+
+  /* do a uniform partition, include the weight function for testing */
+  p4est_partition (p4est, 0, weight_one);
+
+  p4est_check_local_order (p4est, connectivity);
+
+  /* clean up and exit */
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (connectivity);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_partition2.c b/test/test_partition2.c
new file mode 100644
index 0000000..c9a119c
--- /dev/null
+++ b/test/test_partition2.c
@@ -0,0 +1,382 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef P4_TO_P8
+#include <p8est_algorithms.h>
+#include <p8est_communication.h>
+#include <p8est_extended.h>
+#else
+#include <p4est_algorithms.h>
+#include <p4est_communication.h>
+#include <p4est_extended.h>
+#endif
+
+typedef struct
+{
+  p4est_topidx_t      a;
+  int64_t             sum;
+}
+user_data_t;
+
+static int          weight_counter;
+static int          weight_index;
+
+static void
+init_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+         p4est_quadrant_t * quadrant)
+{
+  user_data_t        *data = (user_data_t *) quadrant->p.user_data;
+
+  data->a = which_tree;
+  data->sum = quadrant->x + quadrant->y + quadrant->level;
+}
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  if (quadrant->level >= 6) {
+    return 0;
+  }
+#ifdef P4_TO_P8
+  if (quadrant->level >= 5 && quadrant->z <= P4EST_QUADRANT_LEN (3)) {
+    return 0;
+  }
+#endif
+
+  if (quadrant->x == P4EST_LAST_OFFSET (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->x >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int
+weight_one (p4est_t * p4est, p4est_topidx_t which_tree,
+            p4est_quadrant_t * quadrant)
+{
+  return 1;
+}
+
+static int
+weight_once (p4est_t * p4est, p4est_topidx_t which_tree,
+             p4est_quadrant_t * quadrant)
+{
+  if (weight_counter++ == weight_index) {
+    return 1;
+  }
+
+  return 0;
+}
+
+static int          circle_count;
+
+static void
+circle_init (p4est_t * p4est, p4est_topidx_t which_tree,
+             p4est_quadrant_t * quadrant)
+{
+  int                *idata = (int *) quadrant->p.user_data;
+
+  *idata = ++circle_count;
+}
+
+static void
+test_pertree (p4est_t * p4est, const p4est_gloidx_t * prev_pertree,
+              p4est_gloidx_t * new_pertree)
+{
+  const p4est_topidx_t num_trees = p4est->connectivity->num_trees;
+  p4est_gloidx_t     *pertree;
+
+  P4EST_ASSERT ((size_t) num_trees == p4est->trees->elem_count);
+  if (new_pertree == NULL) {
+    pertree = P4EST_ALLOC (p4est_gloidx_t, num_trees + 1);
+  }
+  else {
+    pertree = new_pertree;
+  }
+  p4est_comm_count_pertree (p4est, pertree);
+  SC_CHECK_ABORT (pertree[num_trees] == p4est->global_num_quadrants,
+                  "pertree check failed");
+  if (prev_pertree != NULL) {
+    SC_CHECK_ABORT (!memcmp (pertree, prev_pertree,
+                             sizeof (p4est_gloidx_t) * (num_trees + 1)),
+                    "pertree now different");
+  }
+  if (new_pertree == NULL) {
+    P4EST_FREE (pertree);
+  }
+}
+
+static void
+test_partition_circle (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity,
+                       p4est_gloidx_t * pertree1, p4est_gloidx_t * pertree2)
+{
+  int                 i, j;
+  int                 num_procs;
+  int                 empty_proc1, empty_proc2;
+  unsigned            crc1, crc2;
+  p4est_gloidx_t      global_num;
+  p4est_locidx_t     *new_counts;
+  p4est_t            *p4est, *copy;
+
+  /* Create a forest and make a copy */
+
+  circle_count = 0;
+  p4est = p4est_new_ext (mpicomm, connectivity, 0, 3, 1,
+                         sizeof (int), circle_init, NULL);
+  num_procs = p4est->mpisize;
+  test_pertree (p4est, NULL, pertree1);
+
+  global_num = p4est->global_num_quadrants;
+  crc1 = p4est_checksum (p4est);
+  copy = p4est_copy (p4est, 1);
+  P4EST_ASSERT (p4est_checksum (copy) == crc1);
+
+  new_counts = P4EST_ALLOC (p4est_locidx_t, num_procs);
+
+  /* Partition with one empty processor */
+  if (num_procs > 1) {
+    P4EST_GLOBAL_INFO ("First circle partition\n");
+    empty_proc1 = num_procs / 3;
+    j = 0;
+    for (i = 0; i < num_procs; ++i) {
+      if (i == empty_proc1) {
+        new_counts[i] = 0;
+      }
+      else {
+        new_counts[i] =
+          p4est_partition_cut_gloidx (global_num, j + 1, num_procs - 1) -
+          p4est_partition_cut_gloidx (global_num, j, num_procs - 1);
+        P4EST_ASSERT (new_counts[i] >= 0);
+        ++j;
+      }
+    }
+    P4EST_ASSERT (j == num_procs - 1);
+    p4est_partition_given (p4est, new_counts);
+    test_pertree (p4est, pertree1, pertree2);
+    crc2 = p4est_checksum (p4est);
+    SC_CHECK_ABORT (crc1 == crc2, "First checksum mismatch");
+  }
+
+  /* Partition with two empty processors */
+  if (num_procs > 2) {
+    P4EST_GLOBAL_INFO ("Second circle partition\n");
+    empty_proc1 = (2 * num_procs) / 3 - 2;
+    empty_proc2 = (2 * num_procs) / 3;
+    j = 0;
+    for (i = 0; i < num_procs; ++i) {
+      if (i == empty_proc1 || i == empty_proc2) {
+        new_counts[i] = 0;
+      }
+      else {
+        new_counts[i] =
+          p4est_partition_cut_gloidx (global_num, j + 1, num_procs - 2) -
+          p4est_partition_cut_gloidx (global_num, j, num_procs - 2);
+        P4EST_ASSERT (new_counts[i] >= 0);
+        ++j;
+      }
+    }
+    P4EST_ASSERT (j == num_procs - 2);
+    p4est_partition_given (p4est, new_counts);
+    test_pertree (p4est, pertree1, pertree2);
+    crc2 = p4est_checksum (p4est);
+    SC_CHECK_ABORT (crc1 == crc2, "Second checksum mismatch");
+  }
+
+  /* Uniform partition */
+  P4EST_GLOBAL_INFO ("Third circle partition\n");
+  p4est_partition (p4est, 0, NULL);
+  test_pertree (p4est, pertree1, pertree2);
+  crc2 = p4est_checksum (p4est);
+  SC_CHECK_ABORT (crc1 == crc2, "Third checksum mismatch");
+  SC_CHECK_ABORT (p4est_is_equal (p4est, copy, 1), "Forest mismatch");
+
+  P4EST_FREE (new_counts);
+  p4est_destroy (copy);
+  p4est_destroy (p4est);
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 rank;
+  int                 num_procs;
+  int                 mpiret;
+  sc_MPI_Comm         mpicomm;
+  p4est_t            *p4est, *copy;
+  p4est_connectivity_t *connectivity;
+  int                 i;
+  p4est_topidx_t      t;
+  size_t              qz;
+  p4est_locidx_t      num_quadrants_on_last;
+  p4est_locidx_t     *num_quadrants_in_proc;
+  p4est_gloidx_t     *pertree1, *pertree2;
+  p4est_quadrant_t   *quad;
+  p4est_tree_t       *tree;
+  user_data_t        *user_data;
+  int64_t             sum;
+  unsigned            crc;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+
+  /* create connectivity and forest structures */
+#ifdef P4_TO_P8
+  connectivity = p8est_connectivity_new_twocubes ();
+#else
+  connectivity = p4est_connectivity_new_corner ();
+#endif
+  p4est = p4est_new_ext (mpicomm, connectivity, 15, 0, 0,
+                         sizeof (user_data_t), init_fn, NULL);
+
+  pertree1 = P4EST_ALLOC (p4est_gloidx_t, p4est->connectivity->num_trees + 1);
+  pertree2 = P4EST_ALLOC (p4est_gloidx_t, p4est->connectivity->num_trees + 1);
+  num_procs = p4est->mpisize;
+  num_quadrants_in_proc = P4EST_ALLOC (p4est_locidx_t, num_procs);
+
+  /* refine and balance to make the number of elements interesting */
+  test_pertree (p4est, NULL, pertree1);
+  p4est_refine (p4est, 1, refine_fn, init_fn);
+  test_pertree (p4est, NULL, pertree1);
+
+  /* Set an arbitrary partition.
+   *
+   * Since this is just a test we assume the global number of
+   * quadrants will fit in an int32_t
+   */
+  num_quadrants_on_last = (p4est_locidx_t) p4est->global_num_quadrants;
+  for (i = 0; i < num_procs - 1; ++i) {
+    num_quadrants_in_proc[i] = (p4est_locidx_t) i + 1;  /* type ok */
+    num_quadrants_on_last -= (p4est_locidx_t) i + 1;    /* type ok */
+  }
+  num_quadrants_in_proc[num_procs - 1] = num_quadrants_on_last;
+  SC_CHECK_ABORT (num_quadrants_on_last > 0,
+                  "Negative number of quadrants on the last processor");
+
+  /* Save a checksum of the original forest */
+  crc = p4est_checksum (p4est);
+
+  /* partition the forest */
+  (void) p4est_partition_given (p4est, num_quadrants_in_proc);
+  test_pertree (p4est, pertree1, pertree2);
+
+  /* Double check that we didn't loose any quads */
+  SC_CHECK_ABORT (crc == p4est_checksum (p4est),
+                  "bad checksum, missing a quad");
+
+  /* count the actual number of quadrants per proc */
+  SC_CHECK_ABORT (num_quadrants_in_proc[rank]
+                  == p4est->local_num_quadrants,
+                  "partition failed, wrong number of quadrants");
+
+  /* check user data content */
+  for (t = p4est->first_local_tree; t <= p4est->last_local_tree; ++t) {
+    tree = p4est_tree_array_index (p4est->trees, t);
+    for (qz = 0; qz < tree->quadrants.elem_count; ++qz) {
+      quad = p4est_quadrant_array_index (&tree->quadrants, qz);
+      user_data = (user_data_t *) quad->p.user_data;
+      sum = quad->x + quad->y + quad->level;
+
+      SC_CHECK_ABORT (user_data->a == t, "bad user_data, a");
+      SC_CHECK_ABORT (user_data->sum == sum, "bad user_data, sum");
+    }
+  }
+
+  /* do a weighted partition with uniform weights */
+  p4est_partition (p4est, 0, weight_one);
+  test_pertree (p4est, pertree1, pertree2);
+  SC_CHECK_ABORT (crc == p4est_checksum (p4est),
+                  "bad checksum after uniformly weighted partition");
+
+  /* copy the p4est */
+  copy = p4est_copy (p4est, 1);
+  SC_CHECK_ABORT (crc == p4est_checksum (copy), "bad checksum after copy");
+
+  /* do a weighted partition with many zero weights */
+  weight_counter = 0;
+  weight_index = (rank == 1) ? 1342 : 0;
+  p4est_partition (copy, 0, weight_once);
+  test_pertree (copy, pertree1, pertree2);
+  SC_CHECK_ABORT (crc == p4est_checksum (copy),
+                  "bad checksum after unevenly weighted partition 1");
+
+  /* do a weighted partition with many zero weights */
+  weight_counter = 0;
+  weight_index = 0;
+  p4est_partition (copy, 0, weight_once);
+  test_pertree (copy, pertree1, pertree2);
+  SC_CHECK_ABORT (crc == p4est_checksum (copy),
+                  "bad checksum after unevenly weighted partition 2");
+
+  /* do a weighted partition with many zero weights
+   *
+   * Since this is just a test we assume the local number of
+   * quadrants will fit in an int
+   */
+  weight_counter = 0;
+  weight_index =
+    (rank == num_procs - 1) ? ((int) copy->local_num_quadrants - 1) : 0;
+  p4est_partition (copy, 0, weight_once);
+  test_pertree (copy, pertree1, pertree2);
+  SC_CHECK_ABORT (crc == p4est_checksum (copy),
+                  "bad checksum after unevenly weighted partition 3");
+
+  /* check user data content */
+  for (t = copy->first_local_tree; t <= copy->last_local_tree; ++t) {
+    tree = p4est_tree_array_index (copy->trees, t);
+    for (qz = 0; qz < tree->quadrants.elem_count; ++qz) {
+      quad = p4est_quadrant_array_index (&tree->quadrants, qz);
+      user_data = (user_data_t *) quad->p.user_data;
+      sum = quad->x + quad->y + quad->level;
+
+      SC_CHECK_ABORT (user_data->a == t, "bad user_data, a");
+      SC_CHECK_ABORT (user_data->sum == sum, "bad user_data, sum");
+    }
+  }
+
+  /* Add another test.  Overwrites pertree1, pertree2 */
+  test_partition_circle (mpicomm, connectivity, pertree1, pertree2);
+
+  /* clean up and exit */
+  P4EST_FREE (pertree1);
+  P4EST_FREE (pertree2);
+  P4EST_FREE (num_quadrants_in_proc);
+  p4est_destroy (p4est);
+  p4est_destroy (copy);
+  p4est_connectivity_destroy (connectivity);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_partition3.c b/test/test_partition3.c
new file mode 100644
index 0000000..84a8287
--- /dev/null
+++ b/test/test_partition3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_partition2.c"
diff --git a/test/test_partition_corr2.c b/test/test_partition_corr2.c
new file mode 100644
index 0000000..0b4ce8c
--- /dev/null
+++ b/test/test_partition_corr2.c
@@ -0,0 +1,185 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef P4_TO_P8
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_vtk.h>
+#else
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_vtk.h>
+#endif
+
+/* typedefs */
+typedef struct
+{
+  p4est_topidx_t      a;
+}
+user_data_t;
+
+/* global variables */
+static int          coarsen_all = 1;
+
+/* functions */
+static void
+init_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+         p4est_quadrant_t * quadrant)
+{
+  user_data_t        *data = (user_data_t *) quadrant->p.user_data;
+
+  data->a = which_tree;
+}
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  if (quadrant->level >= 6) {
+    return 0;
+  }
+#ifdef P4_TO_P8
+  if (quadrant->level >= 5 && quadrant->z <= P4EST_QUADRANT_LEN (3)) {
+    return 0;
+  }
+#endif
+
+  if (quadrant->x == P4EST_LAST_OFFSET (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->x >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int
+coarsen_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+            p4est_quadrant_t * q[])
+{
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (q), "Coarsen invocation");
+
+  return coarsen_all || q[0]->y >= P4EST_ROOT_LEN / 2;
+}
+
+/* main */
+int
+main (int argc, char **argv)
+{
+  int                 rank, num_procs, mpiret, i;
+  sc_MPI_Comm         mpicomm = sc_MPI_COMM_WORLD;
+  p4est_t            *p4est_1tree, *p4est_ntrees;
+  p4est_connectivity_t *connectivity_1tree, *connectivity_ntrees;
+
+  /* initialize MPI and p4est internals */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_size (mpicomm, &num_procs);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* create connectivity */
+#ifdef P4_TO_P8
+  connectivity_1tree = p8est_connectivity_new_unitcube ();
+  connectivity_ntrees = p8est_connectivity_new_twocubes ();
+#else
+  connectivity_1tree = p4est_connectivity_new_unitsquare ();
+  connectivity_ntrees = p4est_connectivity_new_corner ();
+#endif
+
+  /* create p4est structure */
+  p4est_1tree = p4est_new_ext (mpicomm, connectivity_1tree, 15, 0, 0,
+                               sizeof (user_data_t), init_fn, NULL);
+
+  p4est_ntrees = p4est_new_ext (mpicomm, connectivity_ntrees, 15, 0, 0,
+                                sizeof (user_data_t), init_fn, NULL);
+
+  /* write output: new */
+  p4est_vtk_write_file (p4est_1tree, NULL,
+                        P4EST_STRING "_partition_corr_1tree_new");
+  p4est_vtk_write_file (p4est_ntrees, NULL,
+                        P4EST_STRING "_partition_corr_ntrees_new");
+
+  /* refine */
+  p4est_refine (p4est_1tree, 1, refine_fn, init_fn);
+  p4est_refine (p4est_ntrees, 1, refine_fn, init_fn);
+
+  /* write output: refined */
+  p4est_vtk_write_file (p4est_1tree, NULL,
+                        P4EST_STRING "_partition_corr_1tree_refined");
+  p4est_vtk_write_file (p4est_ntrees, NULL,
+                        P4EST_STRING "_partition_corr_ntrees_refined");
+
+  /* run partition and coarsen till one quadrant per tree remains */
+  i = 0;
+  while (p4est_1tree->global_num_quadrants > 1 && i <= P4EST_MAXLEVEL) {
+    (void) p4est_partition_ext (p4est_1tree, 1, NULL);
+    p4est_coarsen (p4est_1tree, 0, coarsen_fn, init_fn);
+    i++;
+  }
+  SC_CHECK_ABORT (p4est_1tree->global_num_quadrants == 1,
+                  "coarsest forest with one tree was not achieved");
+
+  i = 0;
+  while (p4est_ntrees->global_num_quadrants > connectivity_ntrees->num_trees
+         && i <= P4EST_MAXLEVEL) {
+    (void) p4est_partition_ext (p4est_ntrees, 1, NULL);
+    p4est_coarsen (p4est_ntrees, 0, coarsen_fn, init_fn);
+    i++;
+  }
+  SC_CHECK_ABORT (p4est_ntrees->global_num_quadrants
+                  == connectivity_ntrees->num_trees,
+                  "coarsest forest with multiple trees was not achieved");
+
+  /* run partition on coarse forest (one quadrant per tree) once again */
+  (void) p4est_partition_ext (p4est_1tree, 1, NULL);
+  (void) p4est_partition_ext (p4est_ntrees, 1, NULL);
+
+  /* write output: coarsened */
+  p4est_vtk_write_file (p4est_1tree, NULL,
+                        P4EST_STRING "_partition_corr_1tree_coarsened");
+  p4est_vtk_write_file (p4est_ntrees, NULL,
+                        P4EST_STRING "_partition_corr_ntrees_coarsened");
+
+  /* destroy the p4est and its connectivity structure */
+  p4est_destroy (p4est_1tree);
+  p4est_destroy (p4est_ntrees);
+  p4est_connectivity_destroy (connectivity_1tree);
+  p4est_connectivity_destroy (connectivity_ntrees);
+
+  /* clean up and exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_partition_corr3.c b/test/test_partition_corr3.c
new file mode 100644
index 0000000..7657e05
--- /dev/null
+++ b/test/test_partition_corr3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_partition_corr2.c"
diff --git a/test/test_periodic3.c b/test/test_periodic3.c
new file mode 100644
index 0000000..ab01d97
--- /dev/null
+++ b/test/test_periodic3.c
@@ -0,0 +1,310 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p8est.h>
+
+static void
+test_periodic (p8est_connectivity_t * conn)
+{
+  int                 i;
+  int                 iface, nface;
+  int                 iedge, icorner;
+  int                 ft[9];
+  size_t              zz;
+  p4est_topidx_t      itree, ntree;
+  p8est_edge_info_t   ei;
+  p8est_edge_transform_t *et;
+  p8est_corner_info_t ci;
+  p8est_corner_transform_t *ct;
+  sc_array_t         *eta, *cta;
+
+  itree = 0;
+  for (iface = 0; iface < 6; ++iface) {
+    ntree = p8est_find_face_transform (conn, itree, iface, ft);
+    nface = p8est_face_dual[iface];
+    SC_CHECK_ABORT (ntree == itree, "PF tree");
+    for (i = 0; i < 3; ++i) {
+      SC_CHECK_ABORTF (ft[i] == ft[i + 3], "PF axis %d", i);
+    }
+    SC_CHECK_ABORT (ft[6] == 0 && ft[7] == 0, "PF reverse");
+    SC_CHECK_ABORT (ft[8] == 2 * (iface % 2) + nface % 2, "PF code");
+  }
+
+  eta = &ei.edge_transforms;
+  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+  for (iedge = 0; iedge < 12; ++iedge) {
+    p8est_find_edge_transform (conn, itree, iedge, &ei);
+    SC_CHECK_ABORT ((int) ei.iedge == iedge, "PE ei");
+    SC_CHECK_ABORT (eta->elem_count == 1, "PE count");
+    for (zz = 0; zz < eta->elem_count; ++zz) {
+      et = p8est_edge_array_index (eta, zz);
+      SC_CHECK_ABORT (et->ntree == itree, "PE tree");
+      SC_CHECK_ABORT ((int) et->nedge + iedge == 8 * (iedge / 4) + 3,
+                      "PE edge");
+      SC_CHECK_ABORT (et->nflip == 0, "PE flip");
+      SC_CHECK_ABORT (et->corners == et->nedge % 4, "PE corners");
+      SC_CHECK_ABORT ((int) et->naxis[0] == iedge / 4 &&
+                      et->naxis[1] < et->naxis[2] &&
+                      et->naxis[0] + et->naxis[1] + et->naxis[2] == 3,
+                      "PE axis");
+    }
+  }
+  sc_array_reset (eta);
+
+  cta = &ci.corner_transforms;
+  sc_array_init (cta, sizeof (p8est_corner_transform_t));
+  for (icorner = 0; icorner < 8; ++icorner) {
+    p8est_find_corner_transform (conn, itree, icorner, &ci);
+    SC_CHECK_ABORT ((int) ci.icorner == icorner, "PC ci");
+    SC_CHECK_ABORT (cta->elem_count == 1, "PC count");
+    for (zz = 0; zz < cta->elem_count; ++zz) {
+      ct = p8est_corner_array_index (cta, zz);
+      SC_CHECK_ABORT (ct->ntree == itree, "PC tree");
+      SC_CHECK_ABORT (ct->ncorner + icorner == 7, "PC corner");
+    }
+  }
+  sc_array_reset (cta);
+}
+
+/* *INDENT-OFF* */
+static const int
+rotwrap_edges[8][2] =
+{{ 1,  6 }, { 0,  7 }, { 3,  5 }, { 2,  4 },
+ { 3, -1 }, { 2, -1 }, { 0, -1 }, { 1, -1 }};
+
+static const int
+rotwrap_axes[8][2] =
+{{ 0,  1 }, { 0,  1 }, { 0,  1 }, { 0,  1 },
+ { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }};
+
+static const int
+rotwrap_flip[8][2] =
+{{ 0,  0 }, { 0,  0 }, { 0,  1 }, { 0,  1 },
+ { 1, -1 }, { 1, -1 }, { 0, -1 }, { 0, -1 }};
+
+static const int
+rotwrap_corners[8][2] =
+{{ 3, 6 }, { 2, 4 }, { 1, 7 }, { 0, 5 },
+ { 1, 7 }, { 3, 6 }, { 0, 5 }, { 2, 4 }};
+/* *INDENT-ON* */
+
+static void
+test_rotwrap (p8est_connectivity_t * conn)
+{
+  int                 i;
+  int                 iface, nface;
+  int                 iedge, icorner;
+  int                 ft[9];
+  size_t              zz;
+  p4est_topidx_t      itree, ntree;
+  p8est_edge_info_t   ei;
+  p8est_edge_transform_t *et;
+  p8est_corner_info_t ci;
+  p8est_corner_transform_t *ct;
+  sc_array_t         *eta, *cta;
+
+  itree = 0;
+  for (iface = 0; iface < 6; ++iface) {
+    ntree = p8est_find_face_transform (conn, itree, iface, ft);
+    if (iface == 2 || iface == 3) {
+      SC_CHECK_ABORT (ntree == -1, "RF tree");
+      continue;
+    }
+    nface = p8est_face_dual[iface];
+    SC_CHECK_ABORT (ntree == itree, "RF tree");
+    if (iface == 0 || iface == 1) {
+      for (i = 0; i < 3; ++i) {
+        SC_CHECK_ABORTF (ft[i] == (i + 1) % 3, "RFA axis A%d", i);
+        SC_CHECK_ABORTF (ft[i] == ft[i + 3], "RFA axis B%d", i);
+      }
+      SC_CHECK_ABORT (ft[6] == 0 && ft[7] == 0, "RFA reverse");
+    }
+    else {
+      for (i = 0; i < 3; ++i) {
+        SC_CHECK_ABORTF (ft[i] == i, "RFB axis A%d", i);
+      }
+      SC_CHECK_ABORT (ft[0] == ft[4], "RFB axis B0");
+      SC_CHECK_ABORT (ft[1] == ft[3], "RFB axis B1");
+      SC_CHECK_ABORT (ft[2] == ft[5], "RFB axis B2");
+      SC_CHECK_ABORT (ft[6] == (iface != 4) &&
+                      ft[7] == (iface != 5), "RFB reverse");
+    }
+    SC_CHECK_ABORT (ft[8] == 2 * (iface % 2) + nface % 2, "RF code");
+  }
+
+  eta = &ei.edge_transforms;
+  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+  for (iedge = 0; iedge < 12; ++iedge) {
+    p8est_find_edge_transform (conn, itree, iedge, &ei);
+    SC_CHECK_ABORT ((int) ei.iedge == iedge, "RE ei");
+    SC_CHECK_ABORT ((int) eta->elem_count == 2 - (iedge / 4), "RE count AB");
+    for (zz = 0; zz < eta->elem_count; ++zz) {
+      et = p8est_edge_array_index (eta, zz);
+      SC_CHECK_ABORT (et->ntree == itree, "RE tree");
+      SC_CHECK_ABORT ((int) et->nedge == rotwrap_edges[iedge][zz], "RE edge");
+      SC_CHECK_ABORT ((int) et->nflip == rotwrap_flip[iedge][zz], "RE flip");
+      SC_CHECK_ABORT (et->corners == et->nedge % 4, "RE corners");
+      SC_CHECK_ABORT ((int) et->naxis[0] == rotwrap_axes[iedge][zz] &&
+                      et->naxis[1] < et->naxis[2] &&
+                      et->naxis[0] + et->naxis[1] + et->naxis[2] == 3,
+                      "RE axis");
+    }
+  }
+  sc_array_reset (eta);
+
+  cta = &ci.corner_transforms;
+  sc_array_init (cta, sizeof (p8est_corner_transform_t));
+  for (icorner = 0; icorner < 8; ++icorner) {
+    p8est_find_corner_transform (conn, itree, icorner, &ci);
+    SC_CHECK_ABORT ((int) ci.icorner == icorner, "RC ci");
+    SC_CHECK_ABORT (cta->elem_count == 2, "RC count");
+    for (zz = 0; zz < cta->elem_count; ++zz) {
+      ct = p8est_corner_array_index (cta, zz);
+      SC_CHECK_ABORT (ct->ntree == itree, "RC tree");
+      SC_CHECK_ABORT ((int) ct->ncorner == rotwrap_corners[icorner][zz],
+                      "RC corner");
+    }
+  }
+  sc_array_reset (cta);
+}
+
+/* *INDENT-OFF* */
+static const int
+weird_edges[2][2] = {{ 5, 7 }, { 7, 5 }};
+/* *INDENT-ON* */
+
+static void
+test_weird (void)
+{
+  const p4est_topidx_t num_edges = 1, num_ett = 2;
+  const p4est_topidx_t num_corners = 1, num_ctt = 4;
+  int                 i;
+  size_t              zz;
+  p8est_edge_info_t   ei;
+  p8est_edge_transform_t *et;
+  p8est_corner_info_t ci;
+  p8est_corner_transform_t *ct;
+  sc_array_t         *eta, *cta;
+  p8est_connectivity_t *conn;
+
+  conn = p8est_connectivity_new (0, 1,
+                                 num_edges, num_ett, num_corners, num_ctt);
+  for (i = 0; i < 6; ++i) {
+    conn->tree_to_tree[i] = 0;
+    conn->tree_to_face[i] = (int8_t) i;
+  }
+  conn->tree_to_face[4] = 5;
+  conn->tree_to_face[5] = 4;
+
+  for (i = 0; i < 12; ++i) {
+    conn->tree_to_edge[i] = -1;
+  }
+  conn->tree_to_edge[5] = 0;
+  conn->tree_to_edge[7] = 0;
+  conn->edge_to_tree[0] = 0;
+  conn->edge_to_tree[1] = 0;
+  conn->edge_to_edge[0] = 5;
+  conn->edge_to_edge[1] = 19;
+  conn->ett_offset[0] = 0;
+
+  for (i = 0; i < 8; ++i) {
+    conn->tree_to_corner[i] = -1;
+  }
+  conn->tree_to_corner[0] = 0;
+  conn->tree_to_corner[1] = 0;
+  conn->tree_to_corner[4] = 0;
+  conn->tree_to_corner[5] = 0;
+  conn->corner_to_tree[0] = 0;
+  conn->corner_to_tree[1] = 0;
+  conn->corner_to_tree[2] = 0;
+  conn->corner_to_tree[3] = 0;
+  conn->corner_to_corner[0] = 0;
+  conn->corner_to_corner[1] = 1;
+  conn->corner_to_corner[2] = 4;
+  conn->corner_to_corner[3] = 5;
+  conn->ctt_offset[0] = 0;
+
+  P4EST_ASSERT (p8est_connectivity_is_valid (conn));
+
+  eta = &ei.edge_transforms;
+  sc_array_init (eta, sizeof (p8est_edge_transform_t));
+  for (i = 0; i < 2; ++i) {
+    p8est_find_edge_transform (conn, 0, weird_edges[i][0], &ei);
+    SC_CHECK_ABORT ((int) ei.iedge == weird_edges[i][0], "WE ei");
+    SC_CHECK_ABORT (eta->elem_count == 1, "WE count A");
+    for (zz = 0; zz < eta->elem_count; ++zz) {
+      et = p8est_edge_array_index (eta, zz);
+      SC_CHECK_ABORT (et->ntree == 0, "WE tree");
+      SC_CHECK_ABORT ((int) et->nedge == weird_edges[i][1], "WE edge");
+      SC_CHECK_ABORT (et->nflip == 1, "WE flip");
+      SC_CHECK_ABORT (et->corners == et->nedge % 4, "WE corners");
+      SC_CHECK_ABORT (et->naxis[0] == 1 && et->naxis[1] == 0 &&
+                      et->naxis[2] == 2, "WE axis");
+    }
+  }
+  sc_array_reset (eta);
+
+  cta = &ci.corner_transforms;
+  sc_array_init (cta, sizeof (p8est_corner_transform_t));
+  for (i = 0; i < 8; ++i) {
+    p8est_find_corner_transform (conn, 0, i, &ci);
+    SC_CHECK_ABORT ((int) ci.icorner == i, "WC ci");
+    SC_CHECK_ABORT ((int) cta->elem_count == 2 - (i & 0x02), "WC count");
+    for (zz = 0; zz < cta->elem_count; ++zz) {
+      ct = p8est_corner_array_index (cta, zz);
+      SC_CHECK_ABORT (ct->ntree == 0, "WC tree");
+      SC_CHECK_ABORT ((size_t) ct->ncorner == 4 * zz + !(i % 2), "WC corner");
+    }
+  }
+  sc_array_reset (cta);
+
+  p8est_connectivity_destroy (conn);
+}
+
+/*
+ * Purpose of this program is to verify that
+ * p8est_find_edge_transform and p8est_find_corner_transform
+ * work as expected for several periodic connectivities.
+ */
+int
+main (int argc, char **argv)
+{
+  p8est_connectivity_t *conn;
+
+  sc_init (sc_MPI_COMM_NULL, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  conn = p8est_connectivity_new_periodic ();
+  test_periodic (conn);
+  p8est_connectivity_destroy (conn);
+
+  conn = p8est_connectivity_new_rotwrap ();
+  test_rotwrap (conn);
+  p8est_connectivity_destroy (conn);
+
+  test_weird ();
+
+  sc_finalize ();
+
+  return 0;
+}
diff --git a/test/test_plex2.c b/test/test_plex2.c
new file mode 100644
index 0000000..f91f056
--- /dev/null
+++ b/test/test_plex2.c
@@ -0,0 +1,318 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_plex.h>
+#include <p4est_extended.h>
+#include <p4est_bits.h>
+#else
+#include <p8est_plex.h>
+#include <p8est_extended.h>
+#include <p8est_bits.h>
+#endif
+
+#ifdef P4EST_WITH_PETSC
+#include <petsc.h>
+#include <petscdmplex.h>
+#include <petscsf.h>
+
+const char          help[] = "test creating DMPlex from " P4EST_STRING "\n";
+
+static void
+locidx_to_PetscInt (sc_array_t * array)
+{
+  sc_array_t         *newarray;
+  size_t              zz, count = array->elem_count;
+  P4EST_ASSERT (array->elem_size == sizeof (p4est_locidx_t));
+
+  if (sizeof (p4est_locidx_t) == sizeof (PetscInt)) {
+    return;
+  }
+
+  newarray = sc_array_new_size (sizeof (PetscInt), array->elem_count);
+  for (zz = 0; zz < count; zz++) {
+    p4est_locidx_t      il = *((p4est_locidx_t *) sc_array_index (array, zz));
+    PetscInt           *ip = (PetscInt *) sc_array_index (newarray, zz);
+
+    *ip = (PetscInt) il;
+  }
+
+  sc_array_reset (array);
+  sc_array_init_size (array, sizeof (PetscInt), count);
+  sc_array_copy (array, newarray);
+  sc_array_destroy (newarray);
+}
+
+static void
+coords_double_to_PetscScalar (sc_array_t * array)
+{
+  sc_array_t         *newarray;
+  size_t              zz, count = array->elem_count;
+  P4EST_ASSERT (array->elem_size == 3 * sizeof (double));
+
+  if (sizeof (double) == sizeof (PetscScalar)) {
+    return;
+  }
+
+  newarray = sc_array_new_size (3 * sizeof (PetscScalar), array->elem_count);
+  for (zz = 0; zz < count; zz++) {
+    double             *id = (double *) sc_array_index (array, zz);
+    PetscScalar        *ip = (PetscScalar *) sc_array_index (newarray, zz);
+
+    ip[0] = (PetscScalar) id[0];
+    ip[1] = (PetscScalar) id[1];
+    ip[2] = (PetscScalar) id[2];
+  }
+
+  sc_array_reset (array);
+  sc_array_init_size (array, 3 * sizeof (PetscScalar), count);
+  sc_array_copy (array, newarray);
+  sc_array_destroy (newarray);
+}
+
+static void
+locidx_pair_to_PetscSFNode (sc_array_t * array)
+{
+  sc_array_t         *newarray;
+  size_t              zz, count = array->elem_count;
+  P4EST_ASSERT (array->elem_size == 2 * sizeof (p4est_locidx_t));
+
+  newarray = sc_array_new_size (sizeof (PetscSFNode), array->elem_count);
+  for (zz = 0; zz < count; zz++) {
+    p4est_locidx_t     *il = (p4est_locidx_t *) sc_array_index (array, zz);
+    PetscSFNode        *ip = (PetscSFNode *) sc_array_index (newarray, zz);
+
+    ip->rank = (PetscInt) il[0];
+    ip->index = (PetscInt) il[1];
+  }
+
+  sc_array_reset (array);
+  sc_array_init_size (array, sizeof (PetscSFNode), count);
+  sc_array_copy (array, newarray);
+  sc_array_destroy (newarray);
+}
+#endif
+
+#ifndef P4_TO_P8
+static int          refine_level = 5;
+#else
+static int          refine_level = 3;
+#endif
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  if (which_tree == 2 || which_tree == 3) {
+    return 0;
+  }
+
+  cid = p4est_quadrant_child_id (quadrant);
+
+  if (cid == P4EST_CHILDREN - 1 ||
+      (quadrant->x >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2) &&
+       quadrant->y >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#ifdef P4_TO_P8
+       && quadrant->z >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#endif
+      )) {
+    return 1;
+  }
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && cid == 2) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_QUADRANT_LEN (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->y >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 mpisize, mpirank;
+  p4est_t            *p4est;
+  p4est_connectivity_t *conn;
+  sc_array_t         *points_per_dim, *cone_sizes, *cones,
+    *cone_orientations, *coords,
+    *children, *parents, *childids, *leaves, *remotes;
+  p4est_locidx_t      first_local_quad = -1;
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+#ifndef P4_TO_P8
+  conn = p4est_connectivity_new_moebius ();
+#else
+  conn = p8est_connectivity_new_rotcubes ();
+#endif
+  p4est = p4est_new_ext (mpicomm, conn, 0, 1, 1, 0, NULL, NULL);
+  p4est_refine (p4est, 1, refine_fn, NULL);
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+  p4est_partition (p4est, 0, NULL);
+
+  points_per_dim = sc_array_new (sizeof (p4est_locidx_t));
+  cone_sizes = sc_array_new (sizeof (p4est_locidx_t));
+  cones = sc_array_new (sizeof (p4est_locidx_t));
+  cone_orientations = sc_array_new (sizeof (p4est_locidx_t));
+  coords = sc_array_new (3 * sizeof (double));
+  children = sc_array_new (sizeof (p4est_locidx_t));
+  parents = sc_array_new (sizeof (p4est_locidx_t));
+  childids = sc_array_new (sizeof (p4est_locidx_t));
+  leaves = sc_array_new (sizeof (p4est_locidx_t));
+  remotes = sc_array_new (2 * sizeof (p4est_locidx_t));
+
+  p4est_get_plex_data (p4est, P4EST_CONNECT_FULL, (mpisize > 1) ? 2 : 0,
+                       &first_local_quad, points_per_dim, cone_sizes, cones,
+                       cone_orientations, coords, children, parents, childids,
+                       leaves, remotes);
+
+#ifdef P4EST_WITH_PETSC
+  {
+    PetscErrorCode      ierr;
+    DM                  plex, refTree;
+    PetscInt            pStart, pEnd;
+    PetscSection        parentSection;
+    PetscSF             pointSF;
+    size_t              zz, count;
+
+    locidx_to_PetscInt (points_per_dim);
+    locidx_to_PetscInt (cone_sizes);
+    locidx_to_PetscInt (cones);
+    locidx_to_PetscInt (cone_orientations);
+    coords_double_to_PetscScalar (coords);
+    locidx_to_PetscInt (children);
+    locidx_to_PetscInt (parents);
+    locidx_to_PetscInt (childids);
+    locidx_to_PetscInt (leaves);
+    locidx_pair_to_PetscSFNode (remotes);
+
+    P4EST_GLOBAL_PRODUCTION ("Begin PETSc routines\n");
+    ierr = PetscInitialize (&argc, &argv, 0, help);
+    CHKERRQ (ierr);
+
+    ierr = DMPlexCreate (mpicomm, &plex);
+    CHKERRQ (ierr);
+    ierr = DMSetDimension (plex, P4EST_DIM);
+    CHKERRQ (ierr);
+    ierr = DMSetCoordinateDim (plex, 3);
+    CHKERRQ (ierr);
+    ierr = DMPlexCreateFromDAG (plex, P4EST_DIM,
+                                (PetscInt *) points_per_dim->array,
+                                (PetscInt *) cone_sizes->array,
+                                (PetscInt *) cones->array,
+                                (PetscInt *) cone_orientations->array,
+                                (PetscScalar *) coords->array);
+    CHKERRQ (ierr);
+    ierr = PetscSFCreate (mpicomm, &pointSF);
+    CHKERRQ (ierr);
+    ierr =
+      DMPlexCreateDefaultReferenceTree (mpicomm, P4EST_DIM, PETSC_FALSE,
+                                        &refTree);
+    CHKERRQ (ierr);
+    ierr = DMPlexSetReferenceTree (plex, refTree);
+    CHKERRQ (ierr);
+    ierr = DMDestroy (&refTree);
+    CHKERRQ (ierr);
+    ierr = PetscSectionCreate (mpicomm, &parentSection);
+    CHKERRQ (ierr);
+    ierr = DMPlexGetChart (plex, &pStart, &pEnd);
+    CHKERRQ (ierr);
+    ierr = PetscSectionSetChart (parentSection, pStart, pEnd);
+    CHKERRQ (ierr);
+    count = children->elem_count;
+    for (zz = 0; zz < count; zz++) {
+      PetscInt            child =
+        *((PetscInt *) sc_array_index (children, zz));
+
+      ierr = PetscSectionSetDof (parentSection, child, 1);
+      CHKERRQ (ierr);
+    }
+    ierr = PetscSectionSetUp (parentSection);
+    CHKERRQ (ierr);
+    ierr =
+      DMPlexSetTree (plex, parentSection, (PetscInt *) parents->array,
+                     (PetscInt *) childids->array);
+    CHKERRQ (ierr);
+    ierr = PetscSectionDestroy (&parentSection);
+    CHKERRQ (ierr);
+    ierr =
+      PetscSFSetGraph (pointSF, pEnd - pStart, (PetscInt) leaves->elem_count,
+                       (PetscInt *) leaves->array, PETSC_COPY_VALUES,
+                       (PetscSFNode *) remotes->array, PETSC_COPY_VALUES);
+    CHKERRQ (ierr);
+    ierr = DMViewFromOptions (plex, NULL, "-dm_view");
+    CHKERRQ (ierr);
+    /* TODO: test with rigid body modes as in plex ex3 */
+    ierr = DMDestroy (&plex);
+    CHKERRQ (ierr);
+
+    ierr = PetscFinalize ();
+    P4EST_GLOBAL_PRODUCTION ("End   PETSc routines\n");
+  }
+#endif
+
+  sc_array_destroy (points_per_dim);
+  sc_array_destroy (cone_sizes);
+  sc_array_destroy (cones);
+  sc_array_destroy (cone_orientations);
+  sc_array_destroy (coords);
+  sc_array_destroy (children);
+  sc_array_destroy (parents);
+  sc_array_destroy (childids);
+  sc_array_destroy (leaves);
+  sc_array_destroy (remotes);
+
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (conn);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
+
+/* EOF test_plex2.c */
diff --git a/test/test_plex3.c b/test/test_plex3.c
new file mode 100644
index 0000000..d4a4c4d
--- /dev/null
+++ b/test/test_plex3.c
@@ -0,0 +1,27 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_plex2.c"
+
+/* EOF test_plex3.c */
diff --git a/test/test_quadrants2.c b/test/test_quadrants2.c
new file mode 100644
index 0000000..d24c6e5
--- /dev/null
+++ b/test/test_quadrants2.c
@@ -0,0 +1,644 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+
+static int
+refine_none (p4est_t * p4est, p4est_topidx_t which_tree, p4est_quadrant_t * q)
+{
+  return 0;
+}
+
+static int
+refine_some (p4est_t * p4est, p4est_topidx_t which_tree, p4est_quadrant_t * q)
+{
+  if (q->x < P4EST_QUADRANT_LEN (2)) {
+    return q->level <= 4;
+  }
+  else if (q->x < P4EST_QUADRANT_LEN (1)) {
+    return q->level <= 3;
+  }
+  else {
+    return q->level <= 2;
+  }
+}
+
+static int
+coarsen_none (p4est_t * p4est, p4est_topidx_t which_tree,
+              p4est_quadrant_t * q[])
+{
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (q), "Coarsen invocation");
+
+  return 0;
+}
+
+static int
+coarsen_some (p4est_t * p4est, p4est_topidx_t which_tree,
+              p4est_quadrant_t * q[])
+{
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (q), "Coarsen invocation");
+
+  if (q[0]->x < P4EST_QUADRANT_LEN (2)) {
+    return q[0]->level >= 2;
+  }
+  else if (q[0]->x < P4EST_QUADRANT_LEN (1)) {
+    return q[0]->level >= 3;
+  }
+  else {
+    return q[0]->level >= 4;
+  }
+}
+
+static int
+coarsen_all (p4est_t * p4est, p4est_topidx_t which_tree,
+             p4est_quadrant_t * q[])
+{
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (q), "Coarsen invocation");
+
+  return 1;
+}
+
+static void
+check_linear_id (const p4est_quadrant_t * q1, const p4est_quadrant_t * q2)
+{
+  int                 l;
+  int                 comp = p4est_quadrant_compare (q1, q2);
+  int                 level = (int) SC_MIN (q1->level, q2->level);
+  uint64_t            id1 = p4est_quadrant_linear_id (q1, level);
+  uint64_t            id2 = p4est_quadrant_linear_id (q2, level);
+  p4est_quadrant_t    quad, par, anc;
+
+  /* test linear id */
+  if (p4est_quadrant_is_ancestor (q1, q2)) {
+    SC_CHECK_ABORT (id1 == id2 && comp < 0, "Ancestor 1");
+  }
+  else if (p4est_quadrant_is_ancestor (q2, q1)) {
+    SC_CHECK_ABORT (id1 == id2 && comp > 0, "Ancestor 2");
+  }
+  else {
+    SC_CHECK_ABORT ((comp == 0 && id1 == id2) || (comp < 0 && id1 < id2)
+                    || (comp > 0 && id1 > id2), "compare");
+  }
+
+  /* test ancestor and parent functions */
+  par = quad = *q1;
+  for (l = quad.level - 1; l >= 0; --l) {
+    p4est_quadrant_parent (&par, &par);
+    p4est_quadrant_ancestor (&quad, l, &anc);
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&par, &anc), "Ancestor test A");
+  }
+  par = quad = *q2;
+  for (l = quad.level - 1; l >= 0; --l) {
+    p4est_quadrant_parent (&par, &par);
+    p4est_quadrant_ancestor (&quad, l, &anc);
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&par, &anc), "Ancestor test B");
+  }
+}
+
+int
+main (int argc, char **argv)
+{
+  const p4est_qcoord_t qone = 1;
+  int                 mpiret;
+  int                 k;
+  int                 level, mid, cid;
+  int                 id0, id1, id2, id3;
+  int64_t             index1, index2;
+  size_t              iz, jz, incount;
+  p4est_qcoord_t      mh = P4EST_QUADRANT_LEN (P4EST_QMAXLEVEL);
+  p4est_connectivity_t *connectivity;
+  p4est_t            *p4est1;
+  p4est_t            *p4est2;
+  p4est_tree_t       *t1, *t2, tree;
+  p4est_quadrant_t   *p, *q1, *q2;
+  p4est_quadrant_t    r, s;
+  p4est_quadrant_t    c0, c1, c2, c3;
+  p4est_quadrant_t    cv[P4EST_CHILDREN], *cp[P4EST_CHILDREN];
+  p4est_quadrant_t    A, B, C, D, E, F, G, H, I, P, Q;
+  p4est_quadrant_t    a, f, g, h;
+  uint64_t            Aid, Fid;
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  /* create connectivity and forest structures */
+  connectivity = p4est_connectivity_new_unitsquare ();
+  p4est1 = p4est_new_ext (sc_MPI_COMM_SELF, connectivity, 15, 0, 0,
+                          0, NULL, NULL);
+  p4est2 = p4est_new_ext (sc_MPI_COMM_SELF, connectivity, 15, 0, 0,
+                          8, NULL, NULL);
+
+  /* refine the second tree to a uniform level */
+  p4est_refine (p4est1, 1, refine_none, NULL);
+  p4est_refine (p4est2, 1, refine_some, NULL);
+  t1 = p4est_tree_array_index (p4est1->trees, 0);
+  t2 = p4est_tree_array_index (p4est2->trees, 0);
+  SC_CHECK_ABORT (p4est_tree_is_sorted (t1), "is_sorted");
+  SC_CHECK_ABORT (p4est_tree_is_sorted (t2), "is_sorted");
+
+  /* run a bunch of cross-tests */
+  p = NULL;
+  for (iz = 0; iz < t1->quadrants.elem_count; ++iz) {
+    q1 = p4est_quadrant_array_index (&t1->quadrants, iz);
+
+    /* test the index conversion */
+    index1 = p4est_quadrant_linear_id (q1, (int) q1->level);
+    p4est_quadrant_set_morton (&r, (int) q1->level, index1);
+    index2 = p4est_quadrant_linear_id (&r, (int) r.level);
+    SC_CHECK_ABORT (index1 == index2, "index conversion");
+    level = (int) q1->level - 1;
+    if (level >= 0) {
+      index1 = p4est_quadrant_linear_id (q1, level);
+      p4est_quadrant_set_morton (&r, level, index1);
+      index2 = p4est_quadrant_linear_id (&r, level);
+      SC_CHECK_ABORT (index1 == index2, "index conversion");
+    }
+
+    /* test the is_next function */
+    if (p != NULL) {
+      SC_CHECK_ABORT (p4est_quadrant_is_next (p, q1), "is_next");
+    }
+    p = q1;
+
+    /* test the is_family function */
+    p4est_quadrant_children (q1, &c0, &c1, &c2, &c3);
+    SC_CHECK_ABORT (p4est_quadrant_is_family (&c0, &c1, &c2, &c3),
+                    "is_family");
+    SC_CHECK_ABORT (!p4est_quadrant_is_family (&c1, &c0, &c2, &c3),
+                    "is_family");
+    SC_CHECK_ABORT (!p4est_quadrant_is_family (&c0, &c0, &c1, &c2),
+                    "is_family");
+    p4est_quadrant_childrenv (q1, cv);
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c0, &cv[0]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c1, &cv[1]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c2, &cv[2]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c3, &cv[3]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_family (&cv[0], &cv[1], &cv[2], &cv[3]),
+                    "is_family");
+    cp[0] = &cv[0];
+    cp[1] = &cv[1];
+    cp[2] = &cv[2];
+    cp[3] = &cv[3];
+    SC_CHECK_ABORT (p4est_quadrant_is_familypv (cp), "is_family");
+    cv[1] = cv[0];
+    SC_CHECK_ABORT (!p4est_quadrant_is_familyv (cv), "is_family");
+    cp[1] = &c1;
+    SC_CHECK_ABORT (p4est_quadrant_is_familypv (cp), "is_family");
+    cp[2] = &c3;
+    SC_CHECK_ABORT (!p4est_quadrant_is_familypv (cp), "is_family");
+
+    /* test the sibling function */
+    mid = p4est_quadrant_child_id (q1);
+    for (cid = 0; cid < 4; ++cid) {
+      p4est_quadrant_sibling (q1, &r, cid);
+      if (cid != mid) {
+        SC_CHECK_ABORT (p4est_quadrant_is_sibling (q1, &r), "sibling");
+      }
+      else {
+        SC_CHECK_ABORT (p4est_quadrant_is_equal (q1, &r), "sibling");
+      }
+    }
+
+    /* test t1 against itself */
+    for (jz = 0; jz < t1->quadrants.elem_count; ++jz) {
+      q2 = p4est_quadrant_array_index (&t1->quadrants, jz);
+
+      /* test the comparison function */
+      SC_CHECK_ABORT (p4est_quadrant_compare (q1, q2) ==
+                      -p4est_quadrant_compare (q2, q1), "compare");
+      SC_CHECK_ABORT ((p4est_quadrant_compare (q1, q2) == 0) ==
+                      p4est_quadrant_is_equal (q1, q2), "is_equal");
+
+      /* test the descriptive versions of functions */
+      SC_CHECK_ABORT (p4est_quadrant_is_sibling_D (q1, q2) ==
+                      p4est_quadrant_is_sibling (q1, q2), "is_sibling");
+      SC_CHECK_ABORT (p4est_quadrant_is_parent_D (q1, q2) ==
+                      p4est_quadrant_is_parent (q1, q2), "is_parent");
+      SC_CHECK_ABORT (p4est_quadrant_is_parent_D (q2, q1) ==
+                      p4est_quadrant_is_parent (q2, q1), "is_parent");
+      SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (q1, q2) ==
+                      p4est_quadrant_is_ancestor (q1, q2), "is_ancestor");
+      SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (q2, q1) ==
+                      p4est_quadrant_is_ancestor (q2, q1), "is_ancestor");
+      SC_CHECK_ABORT (p4est_quadrant_is_next_D (q1, q2) ==
+                      p4est_quadrant_is_next (q1, q2), "is_next");
+      SC_CHECK_ABORT (p4est_quadrant_is_next_D (q2, q1) ==
+                      p4est_quadrant_is_next (q2, q1), "is_next");
+      p4est_nearest_common_ancestor_D (q1, q2, &r);
+      p4est_nearest_common_ancestor (q1, q2, &s);
+      SC_CHECK_ABORT (p4est_quadrant_is_equal (&r, &s), "common_ancestor");
+      p4est_nearest_common_ancestor_D (q2, q1, &r);
+      p4est_nearest_common_ancestor (q2, q1, &s);
+      SC_CHECK_ABORT (p4est_quadrant_is_equal (&r, &s), "common_ancestor");
+    }
+
+    /* test t1 against t2 */
+    for (jz = 0; jz < t2->quadrants.elem_count; ++jz) {
+      q2 = p4est_quadrant_array_index (&t2->quadrants, jz);
+
+      /* test the comparison function */
+      SC_CHECK_ABORT (p4est_quadrant_compare (q1, q2) ==
+                      -p4est_quadrant_compare (q2, q1), "compare");
+      SC_CHECK_ABORT ((p4est_quadrant_compare (q1, q2) == 0) ==
+                      p4est_quadrant_is_equal (q1, q2), "is_equal");
+
+      /* test the descriptive versions of functions */
+      SC_CHECK_ABORT (p4est_quadrant_is_sibling_D (q1, q2) ==
+                      p4est_quadrant_is_sibling (q1, q2), "is_sibling");
+      SC_CHECK_ABORT (p4est_quadrant_is_parent_D (q1, q2) ==
+                      p4est_quadrant_is_parent (q1, q2), "is_parent");
+      SC_CHECK_ABORT (p4est_quadrant_is_parent_D (q2, q1) ==
+                      p4est_quadrant_is_parent (q2, q1), "is_parent");
+      SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (q1, q2) ==
+                      p4est_quadrant_is_ancestor (q1, q2), "is_ancestor");
+      SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (q2, q1) ==
+                      p4est_quadrant_is_ancestor (q2, q1), "is_ancestor");
+      SC_CHECK_ABORT (p4est_quadrant_is_next_D (q1, q2) ==
+                      p4est_quadrant_is_next (q1, q2), "is_next");
+      SC_CHECK_ABORT (p4est_quadrant_is_next_D (q2, q1) ==
+                      p4est_quadrant_is_next (q2, q1), "is_next");
+      p4est_nearest_common_ancestor_D (q1, q2, &r);
+      p4est_nearest_common_ancestor (q1, q2, &s);
+      SC_CHECK_ABORT (p4est_quadrant_is_equal (&r, &s), "common_ancestor");
+      p4est_nearest_common_ancestor_D (q2, q1, &r);
+      p4est_nearest_common_ancestor (q2, q1, &s);
+      SC_CHECK_ABORT (p4est_quadrant_is_equal (&r, &s), "common_ancestor");
+    }
+  }
+
+  p = NULL;
+  for (iz = 0; iz < t2->quadrants.elem_count; ++iz) {
+    q1 = p4est_quadrant_array_index (&t2->quadrants, iz);
+
+    /* test the is_next function */
+    if (p != NULL) {
+      SC_CHECK_ABORT (p4est_quadrant_is_next (p, q1), "is_next");
+    }
+    p = q1;
+  }
+
+  /* test the coarsen function */
+  p4est_coarsen (p4est1, 1, coarsen_none, NULL);
+  p4est_coarsen (p4est1, 1, coarsen_all, NULL);
+  p4est_coarsen (p4est2, 1, coarsen_some, NULL);
+
+  /* test the linearize algorithm */
+  incount = t2->quadrants.elem_count;
+  (void) p4est_linearize_tree (p4est2, t2);
+  SC_CHECK_ABORT (incount == t2->quadrants.elem_count, "linearize");
+
+  /* this is user_data neutral only when p4est1->data_size == 0 */
+  sc_array_init (&tree.quadrants, sizeof (p4est_quadrant_t));
+  sc_array_resize (&tree.quadrants, 18);
+  q1 = p4est_quadrant_array_index (&tree.quadrants, 0);
+  q2 = p4est_quadrant_array_index (&t2->quadrants, 0);
+  *q1 = *q2;
+  q2 = p4est_quadrant_array_index (&t2->quadrants, 1);
+  for (k = 0; k < 3; ++k) {
+    q1 = p4est_quadrant_array_index (&tree.quadrants, (size_t) (k + 1));
+    *q1 = *q2;
+    q1->level = (int8_t) (q1->level + k);
+  }
+  for (k = 0; k < 10; ++k) {
+    q1 = p4est_quadrant_array_index (&tree.quadrants, (size_t) (k + 4));
+    q2 = p4est_quadrant_array_index (&t2->quadrants, (size_t) (k + 3));
+    *q1 = *q2;
+    q1->level = (int8_t) (q1->level + k);
+  }
+  for (k = 0; k < 4; ++k) {
+    q1 = p4est_quadrant_array_index (&tree.quadrants, (size_t) (k + 14));
+    q2 = p4est_quadrant_array_index (&t2->quadrants, (size_t) (k + 12));
+    *q1 = *q2;
+    q1->level = (int8_t) (q1->level + 10 + k);
+  }
+  tree.maxlevel = 0;
+  for (k = 0; k <= P4EST_QMAXLEVEL; ++k) {
+    tree.quadrants_per_level[k] = 0;
+  }
+  for (; k <= P4EST_MAXLEVEL; ++k) {
+    tree.quadrants_per_level[k] = -1;
+  }
+  incount = tree.quadrants.elem_count;
+  for (iz = 0; iz < incount; ++iz) {
+    q1 = p4est_quadrant_array_index (&tree.quadrants, iz);
+    ++tree.quadrants_per_level[q1->level];
+    tree.maxlevel = (int8_t) SC_MAX (tree.maxlevel, q1->level);
+  }
+  SC_CHECK_ABORT (!p4est_tree_is_linear (&tree), "is_linear");
+  (void) p4est_linearize_tree (p4est1, &tree);
+  SC_CHECK_ABORT (incount - 3 == tree.quadrants.elem_count, "linearize");
+  sc_array_reset (&tree.quadrants);
+
+  /* create a partial tree and check overlap */
+  sc_array_resize (&tree.quadrants, 3);
+  q1 = p4est_quadrant_array_index (&tree.quadrants, 0);
+  p4est_quadrant_set_morton (q1, 1, 1);
+  q1 = p4est_quadrant_array_index (&tree.quadrants, 1);
+  p4est_quadrant_set_morton (q1, 2, 8);
+  q1 = p4est_quadrant_array_index (&tree.quadrants, 2);
+  p4est_quadrant_set_morton (q1, 2, 9);
+  for (k = 0; k <= P4EST_QMAXLEVEL; ++k) {
+    tree.quadrants_per_level[k] = 0;
+  }
+  for (; k <= P4EST_MAXLEVEL; ++k) {
+    tree.quadrants_per_level[k] = -1;
+  }
+  tree.quadrants_per_level[1] = 1;
+  tree.quadrants_per_level[2] = 2;
+  tree.maxlevel = 2;
+  p4est_quadrant_first_descendant (p4est_quadrant_array_index
+                                   (&tree.quadrants, 0), &tree.first_desc,
+                                   P4EST_QMAXLEVEL);
+  p4est_quadrant_last_descendant (p4est_quadrant_array_index
+                                  (&tree.quadrants,
+                                   tree.quadrants.elem_count - 1),
+                                  &tree.last_desc, P4EST_QMAXLEVEL);
+  SC_CHECK_ABORT (p4est_tree_is_complete (&tree), "is_complete");
+
+  p4est_quadrant_set_morton (&D, 0, 0);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &D), "overlaps 0");
+
+  p4est_quadrant_set_morton (&A, 1, 0);
+  SC_CHECK_ABORT (!p4est_quadrant_overlaps_tree (&tree, &A), "overlaps 1");
+  p4est_quadrant_set_morton (&A, 1, 1);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &A), "overlaps 2");
+  p4est_quadrant_set_morton (&A, 1, 2);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &A), "overlaps 3");
+  p4est_quadrant_set_morton (&A, 1, 3);
+  SC_CHECK_ABORT (!p4est_quadrant_overlaps_tree (&tree, &A), "overlaps 4");
+
+  p4est_quadrant_set_morton (&B, 3, 13);
+  SC_CHECK_ABORT (!p4est_quadrant_overlaps_tree (&tree, &B), "overlaps 5");
+  p4est_quadrant_set_morton (&B, 3, 25);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &B), "overlaps 6");
+  p4est_quadrant_set_morton (&B, 3, 39);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &B), "overlaps 7");
+  p4est_quadrant_set_morton (&B, 3, 40);
+  SC_CHECK_ABORT (!p4est_quadrant_overlaps_tree (&tree, &B), "overlaps 8");
+
+  p4est_quadrant_set_morton (&C, 4, 219);
+  SC_CHECK_ABORT (!p4est_quadrant_overlaps_tree (&tree, &C), "overlaps 9");
+
+  sc_array_reset (&tree.quadrants);
+
+  /* destroy the p4est and its connectivity structure */
+  p4est_destroy (p4est1);
+  p4est_destroy (p4est2);
+  p4est_connectivity_destroy (connectivity);
+
+  /* This will test the ability to address negative quadrants */
+  P4EST_QUADRANT_INIT (&A);
+  P4EST_QUADRANT_INIT (&B);
+  P4EST_QUADRANT_INIT (&C);
+  P4EST_QUADRANT_INIT (&D);
+  P4EST_QUADRANT_INIT (&E);
+  P4EST_QUADRANT_INIT (&F);
+  P4EST_QUADRANT_INIT (&G);
+  P4EST_QUADRANT_INIT (&H);
+  P4EST_QUADRANT_INIT (&I);
+  P4EST_QUADRANT_INIT (&P);
+  P4EST_QUADRANT_INIT (&Q);
+
+  A.x = -qone << P4EST_MAXLEVEL;
+  A.y = -qone << P4EST_MAXLEVEL;
+  A.level = 0;
+
+  B.x = qone << P4EST_MAXLEVEL;
+  B.y = -qone << P4EST_MAXLEVEL;
+  B.level = 0;
+
+  C.x = -qone << P4EST_MAXLEVEL;
+  C.y = qone << P4EST_MAXLEVEL;
+  C.level = 0;
+
+  D.x = qone << P4EST_MAXLEVEL;
+  D.y = qone << P4EST_MAXLEVEL;
+  D.level = 0;
+
+  /* this one is outside the 3x3 box */
+  E.x = -qone << (P4EST_MAXLEVEL + 1);
+  E.y = -qone;
+  E.level = 0;
+
+  F.x = P4EST_ROOT_LEN + (P4EST_ROOT_LEN - mh);
+  F.y = P4EST_ROOT_LEN + (P4EST_ROOT_LEN - mh);
+  F.level = P4EST_QMAXLEVEL;
+
+  G.x = -mh;
+  G.y = -mh;
+  G.level = P4EST_QMAXLEVEL;
+
+  H.x = -qone << (P4EST_MAXLEVEL - 1);
+  H.y = -qone << (P4EST_MAXLEVEL - 1);
+  H.level = 1;
+
+  I.x = -qone << P4EST_MAXLEVEL;
+  I.y = -qone << (P4EST_MAXLEVEL - 1);
+  I.level = 1;
+
+  check_linear_id (&A, &A);
+  check_linear_id (&A, &B);
+  check_linear_id (&A, &C);
+  check_linear_id (&A, &D);
+  /* check_linear_id (&A, &E); */
+  check_linear_id (&A, &F);
+  check_linear_id (&A, &G);
+  check_linear_id (&A, &H);
+  check_linear_id (&A, &I);
+
+  check_linear_id (&B, &A);
+  check_linear_id (&B, &B);
+  check_linear_id (&B, &C);
+  check_linear_id (&B, &D);
+  /* check_linear_id (&B, &E); */
+  check_linear_id (&B, &F);
+  check_linear_id (&B, &G);
+  check_linear_id (&B, &H);
+  check_linear_id (&B, &I);
+
+  check_linear_id (&D, &A);
+  check_linear_id (&D, &B);
+  check_linear_id (&D, &C);
+  check_linear_id (&D, &D);
+  /* check_linear_id (&D, &E); */
+  check_linear_id (&D, &F);
+  check_linear_id (&D, &G);
+  check_linear_id (&D, &H);
+  check_linear_id (&D, &I);
+
+  check_linear_id (&G, &A);
+  check_linear_id (&G, &B);
+  check_linear_id (&G, &C);
+  check_linear_id (&G, &D);
+  /* check_linear_id (&G, &E); */
+  check_linear_id (&G, &F);
+  check_linear_id (&G, &G);
+  check_linear_id (&G, &H);
+  check_linear_id (&G, &I);
+
+  check_linear_id (&I, &A);
+  check_linear_id (&I, &B);
+  check_linear_id (&I, &C);
+  check_linear_id (&I, &D);
+  /* check_linear_id (&I, &E); */
+  check_linear_id (&I, &F);
+  check_linear_id (&I, &G);
+  check_linear_id (&I, &H);
+  check_linear_id (&I, &I);
+
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&A) == 1, "is_extended A");
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&B) == 1, "is_extended B");
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&C) == 1, "is_extended C");
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&D) == 1, "is_extended D");
+  SC_CHECK_ABORT (!p4est_quadrant_is_extended (&E) == 1, "!is_extended E");
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&F) == 1, "is_extended F");
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&G) == 1, "is_extended G");
+
+  SC_CHECK_ABORT (p4est_quadrant_compare (&A, &A) == 0, "compare");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&A, &B) > 0, "compare");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&B, &A) < 0, "compare");
+
+  SC_CHECK_ABORT (p4est_quadrant_compare (&F, &F) == 0, "compare");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&G, &F) > 0, "compare");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&F, &G) < 0, "compare");
+
+  A.p.which_tree = 0;
+  B.p.piggy1.which_tree = 0;
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&A, &A) == 0,
+                  "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&A, &B) > 0, "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&B, &A) < 0, "compare_piggy");
+
+  F.p.piggy2.which_tree = 0;
+  G.p.which_tree = 0;
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&F, &F) == 0,
+                  "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&G, &F) > 0, "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&F, &G) < 0, "compare_piggy");
+
+  F.p.piggy1.which_tree = (p4est_topidx_t) P4EST_TOPIDX_MAX - 3;
+  G.p.piggy2.which_tree = (p4est_topidx_t) P4EST_TOPIDX_MAX / 2;
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&F, &F) == 0,
+                  "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&G, &F) < 0, "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&F, &G) > 0, "compare_piggy");
+
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&A, &A) == 1, "is_equal");
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&F, &F) == 1, "is_equal");
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&G, &G) == 1, "is_equal");
+
+  /* Not sure if these make sense because D, O and A are all level 0 */
+#if 0
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling (&D, &O) == 1, "is_sibling");
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling (&D, &A) == 0, "is_sibling");
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling_D (&D, &O) == 1, "is_sibling_D");
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling_D (&D, &A) == 0, "is_sibling_D");
+#endif
+
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling (&I, &H) == 1, "is_sibling");
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling (&I, &G) == 0, "is_sibling");
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling_D (&I, &H) == 1, "is_sibling_D");
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling_D (&I, &G) == 0, "is_sibling_D");
+
+  SC_CHECK_ABORT (p4est_quadrant_is_parent (&A, &H) == 1, "is_parent");
+  SC_CHECK_ABORT (p4est_quadrant_is_parent (&H, &A) == 0, "is_parent");
+  SC_CHECK_ABORT (p4est_quadrant_is_parent (&A, &D) == 0, "is_parent");
+  SC_CHECK_ABORT (p4est_quadrant_is_parent_D (&A, &H) == 1, "is_parent_D");
+
+  SC_CHECK_ABORT (p4est_quadrant_is_ancestor (&A, &G) == 1, "is_ancestor");
+  SC_CHECK_ABORT (p4est_quadrant_is_ancestor (&G, &A) == 0, "is_ancestor");
+
+  SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (&A, &G) == 1,
+                  "is_ancestor_D");
+  SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (&G, &A) == 0,
+                  "is_ancestor_D");
+
+  /* SC_CHECK_ABORT (p4est_quadrant_is_next (&F, &E) == 1, "is_next"); */
+  SC_CHECK_ABORT (p4est_quadrant_is_next (&A, &H) == 0, "is_next");
+  /* SC_CHECK_ABORT (p4est_quadrant_is_next_D (&F, &E) == 1, "is_next_D"); */
+  SC_CHECK_ABORT (p4est_quadrant_is_next_D (&A, &H) == 0, "is_next_D");
+
+  p4est_quadrant_parent (&H, &a);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&A, &a) == 1, "parent");
+
+  p4est_quadrant_sibling (&I, &h, 3);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&H, &h) == 1, "sibling");
+
+  p4est_quadrant_children (&A, &c0, &c1, &c2, &c3);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&c2, &I) == 1, "children");
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&c3, &H) == 1, "children");
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&c3, &G) == 0, "children");
+
+  SC_CHECK_ABORT (p4est_quadrant_is_family (&c0, &c1, &c2, &c3) == 1,
+                  "is_family");
+  id0 = p4est_quadrant_child_id (&c0);
+  id1 = p4est_quadrant_child_id (&c1);
+  id2 = p4est_quadrant_child_id (&c2);
+  id3 = p4est_quadrant_child_id (&c3);
+  SC_CHECK_ABORT (id0 == 0 && id1 == 1 && id2 == 2 && id3 == 3, "child_id");
+  SC_CHECK_ABORT (p4est_quadrant_child_id (&G) == 3, "child_id");
+
+  p4est_quadrant_first_descendant (&A, &c1, 1);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&c0, &c1) == 1,
+                  "first_descendant");
+
+  p4est_quadrant_last_descendant (&A, &g, P4EST_QMAXLEVEL);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&G, &g) == 1, "last_descendant");
+
+  Fid = p4est_quadrant_linear_id (&F, P4EST_QMAXLEVEL);
+  p4est_quadrant_set_morton (&f, P4EST_QMAXLEVEL, Fid);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&F, &f) == 1,
+                  "set_morton/linear_id");
+
+  Aid = p4est_quadrant_linear_id (&A, 0);
+  p4est_quadrant_set_morton (&a, 0, Aid);
+  SC_CHECK_ABORT (Aid == 15, "linear_id");
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&A, &a) == 1,
+                  "set_morton/linear_id");
+
+  p4est_nearest_common_ancestor (&I, &H, &a);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&A, &a) == 1, "ancestor");
+
+  p4est_nearest_common_ancestor_D (&I, &H, &a);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&A, &a) == 1, "ancestor_D");
+
+  for (k = 0; k < 16; ++k) {
+    if (k != 4 && k != 6 && k != 8 && k != 9 && k != 12 && k != 13 && k != 14) {
+      p4est_quadrant_set_morton (&E, 0, (uint64_t) k);
+    }
+  }
+  p4est_quadrant_set_morton (&P, 0, 10);
+  p4est_quadrant_set_morton (&Q, 0, 11);
+  SC_CHECK_ABORT (p4est_quadrant_is_next (&P, &Q), "is_next");
+  SC_CHECK_ABORT (!p4est_quadrant_is_next (&A, &Q), "is_next");
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_quadrants3.c b/test/test_quadrants3.c
new file mode 100644
index 0000000..25f4b50
--- /dev/null
+++ b/test/test_quadrants3.c
@@ -0,0 +1,693 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p4est_to_p8est.h>
+
+static int
+refine_none (p4est_t * p4est, p4est_topidx_t which_tree, p4est_quadrant_t * q)
+{
+  return 0;
+}
+
+static int
+refine_some (p4est_t * p4est, p4est_topidx_t which_tree, p4est_quadrant_t * q)
+{
+  if (q->x < P4EST_QUADRANT_LEN (2)) {
+    return q->level <= 4;
+  }
+  else if (q->x < P4EST_QUADRANT_LEN (1)) {
+    return q->level <= 3;
+  }
+  else {
+    return q->level <= 2;
+  }
+}
+
+static int
+coarsen_none (p4est_t * p4est, p4est_topidx_t which_tree,
+              p4est_quadrant_t * q[])
+{
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (q), "Coarsen invocation");
+
+  return 0;
+}
+
+static int
+coarsen_some (p4est_t * p4est, p4est_topidx_t which_tree,
+              p4est_quadrant_t * q[])
+{
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (q), "Coarsen invocation");
+
+  if (q[0]->x < P4EST_QUADRANT_LEN (2)) {
+    return q[0]->level >= 2;
+  }
+  else if (q[0]->x < P4EST_QUADRANT_LEN (1)) {
+    return q[0]->level >= 3;
+  }
+  else {
+    return q[0]->level >= 4;
+  }
+}
+
+static int
+coarsen_all (p4est_t * p4est, p4est_topidx_t which_tree,
+             p4est_quadrant_t * q[])
+{
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (q), "Coarsen invocation");
+
+  return 1;
+}
+
+static void
+check_linear_id (const p4est_quadrant_t * q1, const p4est_quadrant_t * q2)
+{
+  int                 l;
+  int                 comp = p4est_quadrant_compare (q1, q2);
+  int                 level = (int) SC_MIN (q1->level, q2->level);
+  uint64_t            id1 = p4est_quadrant_linear_id (q1, level);
+  uint64_t            id2 = p4est_quadrant_linear_id (q2, level);
+  p4est_quadrant_t    quad, par, anc;
+
+  /* test linear id */
+  if (p4est_quadrant_is_ancestor (q1, q2)) {
+    SC_CHECK_ABORT (id1 == id2 && comp < 0, "Ancestor 1");
+  }
+  else if (p4est_quadrant_is_ancestor (q2, q1)) {
+    SC_CHECK_ABORT (id1 == id2 && comp > 0, "Ancestor 2");
+  }
+  else {
+    SC_CHECK_ABORT ((comp == 0 && id1 == id2) || (comp < 0 && id1 < id2)
+                    || (comp > 0 && id1 > id2), "compare");
+  }
+
+  /* test ancestor and parent functions */
+  par = quad = *q1;
+  for (l = quad.level - 1; l >= 0; --l) {
+    p4est_quadrant_parent (&par, &par);
+    p4est_quadrant_ancestor (&quad, l, &anc);
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&par, &anc), "Ancestor test A");
+  }
+  par = quad = *q2;
+  for (l = quad.level - 1; l >= 0; --l) {
+    p4est_quadrant_parent (&par, &par);
+    p4est_quadrant_ancestor (&quad, l, &anc);
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&par, &anc), "Ancestor test B");
+  }
+}
+
+int
+main (int argc, char **argv)
+{
+  const p4est_qcoord_t qone = 1;
+  int                 mpiret;
+  int                 k;
+  int                 level, mid, cid;
+  int                 id0, id1, id2, id3;
+  int64_t             index1, index2;
+  size_t              iz, jz, incount;
+  p4est_qcoord_t      mh = P4EST_QUADRANT_LEN (P4EST_QMAXLEVEL);
+  p4est_connectivity_t *connectivity;
+  p4est_t            *p4est1;
+  p4est_t            *p4est2;
+  p4est_tree_t       *t1, *t2, tree;
+  p4est_quadrant_t   *p, *q1, *q2;
+  p4est_quadrant_t    r, s;
+  p4est_quadrant_t    c0, c1, c2, c3, c4, c5, c6, c7;
+  p4est_quadrant_t    cv[P4EST_CHILDREN], *cp[P4EST_CHILDREN];
+  p4est_quadrant_t    A, B, C, D, E, F, G, H, I, P, Q;
+  p4est_quadrant_t    a, f, g, h;
+  uint64_t            Aid, Fid;
+  const int           indices[27] = { 0, 1, 2, 3, 4, 5, 6, 7,
+    7, 9, 11, 13, 18, 19, 22, 23, 27, 31,
+    36, 37, 38, 39, 45, 47, 54, 55, 63
+  };
+
+  /* initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+
+  /* create connectivity and forest structures */
+  connectivity = p8est_connectivity_new_unitcube ();
+  p4est1 = p4est_new_ext (sc_MPI_COMM_SELF, connectivity, 15, 0, 0,
+                          0, NULL, NULL);
+  p4est2 = p4est_new_ext (sc_MPI_COMM_SELF, connectivity, 15, 0, 0,
+                          8, NULL, NULL);
+
+  /* refine the second tree to a uniform level */
+  p4est_refine (p4est1, 1, refine_none, NULL);
+  p4est_refine (p4est2, 1, refine_some, NULL);
+  t1 = p4est_tree_array_index (p4est1->trees, 0);
+  t2 = p4est_tree_array_index (p4est2->trees, 0);
+  SC_CHECK_ABORT (p4est_tree_is_sorted (t1), "is_sorted");
+  SC_CHECK_ABORT (p4est_tree_is_sorted (t2), "is_sorted");
+
+  /* run a bunch of cross-tests */
+  p = NULL;
+  for (iz = 0; iz < t1->quadrants.elem_count; ++iz) {
+    q1 = p4est_quadrant_array_index (&t1->quadrants, iz);
+
+    /* test the index conversion */
+    index1 = p4est_quadrant_linear_id (q1, (int) q1->level);
+    p4est_quadrant_set_morton (&r, (int) q1->level, index1);
+    index2 = p4est_quadrant_linear_id (&r, (int) r.level);
+    SC_CHECK_ABORT (index1 == index2, "index conversion");
+    level = (int) q1->level - 1;
+    if (level >= 0) {
+      index1 = p4est_quadrant_linear_id (q1, level);
+      p4est_quadrant_set_morton (&r, level, index1);
+      index2 = p4est_quadrant_linear_id (&r, level);
+      SC_CHECK_ABORT (index1 == index2, "index conversion");
+    }
+
+    /* test the is_next function */
+    if (p != NULL) {
+      SC_CHECK_ABORT (p4est_quadrant_is_next (p, q1), "is_next");
+    }
+    p = q1;
+
+    /* test the is_family function */
+    p8est_quadrant_children (q1, &c0, &c1, &c2, &c3, &c4, &c5, &c6, &c7);
+    SC_CHECK_ABORT (p8est_quadrant_is_family
+                    (&c0, &c1, &c2, &c3, &c4, &c5, &c6, &c7), "is_family");
+    SC_CHECK_ABORT (!p8est_quadrant_is_family
+                    (&c1, &c0, &c2, &c3, &c4, &c5, &c6, &c7), "is_family");
+    SC_CHECK_ABORT (!p8est_quadrant_is_family
+                    (&c0, &c1, &c2, &c3, &c4, &c5, &c5, &c7), "is_family");
+    p4est_quadrant_childrenv (q1, cv);
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c0, &cv[0]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c1, &cv[1]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c2, &cv[2]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c3, &cv[3]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c4, &cv[4]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c5, &cv[5]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c6, &cv[6]), "is_family");
+    SC_CHECK_ABORT (p4est_quadrant_is_equal (&c7, &cv[7]), "is_family");
+    SC_CHECK_ABORT (p8est_quadrant_is_family (&cv[0], &cv[1], &cv[2], &cv[3],
+                                              &cv[4], &cv[5], &cv[6], &cv[7]),
+                    "is_family");
+    cp[0] = &cv[0];
+    cp[1] = &cv[1];
+    cp[2] = &cv[2];
+    cp[3] = &cv[3];
+    cp[4] = &cv[4];
+    cp[5] = &cv[5];
+    cp[6] = &cv[6];
+    cp[7] = &cv[7];
+    SC_CHECK_ABORT (p4est_quadrant_is_familypv (cp), "is_family");
+    cv[1] = cv[0];
+    SC_CHECK_ABORT (!p4est_quadrant_is_familyv (cv), "is_family");
+    cp[1] = &c1;
+    SC_CHECK_ABORT (p4est_quadrant_is_familypv (cp), "is_family");
+    cp[6] = &c7;
+    SC_CHECK_ABORT (!p4est_quadrant_is_familypv (cp), "is_family");
+
+    /* test the sibling function */
+    mid = p4est_quadrant_child_id (q1);
+    for (cid = 0; cid < P4EST_CHILDREN; ++cid) {
+      p4est_quadrant_sibling (q1, &r, cid);
+      if (cid != mid) {
+        SC_CHECK_ABORT (p4est_quadrant_is_sibling (q1, &r), "sibling");
+      }
+      else {
+        SC_CHECK_ABORT (p4est_quadrant_is_equal (q1, &r), "sibling");
+      }
+    }
+
+    /* test t1 against itself */
+    for (jz = 0; jz < t1->quadrants.elem_count; ++jz) {
+      q2 = p4est_quadrant_array_index (&t1->quadrants, jz);
+
+      /* test the comparison function */
+      SC_CHECK_ABORT (p4est_quadrant_compare (q1, q2) ==
+                      -p4est_quadrant_compare (q2, q1), "compare");
+      SC_CHECK_ABORT ((p4est_quadrant_compare (q1, q2) == 0) ==
+                      p4est_quadrant_is_equal (q1, q2), "is_equal");
+
+      /* test the descriptive versions of functions */
+      SC_CHECK_ABORT (p4est_quadrant_is_sibling_D (q1, q2) ==
+                      p4est_quadrant_is_sibling (q1, q2), "is_sibling");
+      SC_CHECK_ABORT (p4est_quadrant_is_parent_D (q1, q2) ==
+                      p4est_quadrant_is_parent (q1, q2), "is_parent");
+      SC_CHECK_ABORT (p4est_quadrant_is_parent_D (q2, q1) ==
+                      p4est_quadrant_is_parent (q2, q1), "is_parent");
+      SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (q1, q2) ==
+                      p4est_quadrant_is_ancestor (q1, q2), "is_ancestor");
+      SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (q2, q1) ==
+                      p4est_quadrant_is_ancestor (q2, q1), "is_ancestor");
+      SC_CHECK_ABORT (p4est_quadrant_is_next_D (q1, q2) ==
+                      p4est_quadrant_is_next (q1, q2), "is_next");
+      SC_CHECK_ABORT (p4est_quadrant_is_next_D (q2, q1) ==
+                      p4est_quadrant_is_next (q2, q1), "is_next");
+      p4est_nearest_common_ancestor_D (q1, q2, &r);
+      p4est_nearest_common_ancestor (q1, q2, &s);
+      SC_CHECK_ABORT (p4est_quadrant_is_equal (&r, &s), "common_ancestor");
+      p4est_nearest_common_ancestor_D (q2, q1, &r);
+      p4est_nearest_common_ancestor (q2, q1, &s);
+      SC_CHECK_ABORT (p4est_quadrant_is_equal (&r, &s), "common_ancestor");
+    }
+
+    /* test t1 against t2 */
+    for (jz = 0; jz < t2->quadrants.elem_count; ++jz) {
+      q2 = p4est_quadrant_array_index (&t2->quadrants, jz);
+
+      /* test the comparison function */
+      SC_CHECK_ABORT (p4est_quadrant_compare (q1, q2) ==
+                      -p4est_quadrant_compare (q2, q1), "compare");
+      SC_CHECK_ABORT ((p4est_quadrant_compare (q1, q2) == 0) ==
+                      p4est_quadrant_is_equal (q1, q2), "is_equal");
+
+      /* test the descriptive versions of functions */
+      SC_CHECK_ABORT (p4est_quadrant_is_sibling_D (q1, q2) ==
+                      p4est_quadrant_is_sibling (q1, q2), "is_sibling");
+      SC_CHECK_ABORT (p4est_quadrant_is_parent_D (q1, q2) ==
+                      p4est_quadrant_is_parent (q1, q2), "is_parent");
+      SC_CHECK_ABORT (p4est_quadrant_is_parent_D (q2, q1) ==
+                      p4est_quadrant_is_parent (q2, q1), "is_parent");
+      SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (q1, q2) ==
+                      p4est_quadrant_is_ancestor (q1, q2), "is_ancestor");
+      SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (q2, q1) ==
+                      p4est_quadrant_is_ancestor (q2, q1), "is_ancestor");
+      SC_CHECK_ABORT (p4est_quadrant_is_next_D (q1, q2) ==
+                      p4est_quadrant_is_next (q1, q2), "is_next");
+      SC_CHECK_ABORT (p4est_quadrant_is_next_D (q2, q1) ==
+                      p4est_quadrant_is_next (q2, q1), "is_next");
+      p4est_nearest_common_ancestor_D (q1, q2, &r);
+      p4est_nearest_common_ancestor (q1, q2, &s);
+      SC_CHECK_ABORT (p4est_quadrant_is_equal (&r, &s), "common_ancestor");
+      p4est_nearest_common_ancestor_D (q2, q1, &r);
+      p4est_nearest_common_ancestor (q2, q1, &s);
+      SC_CHECK_ABORT (p4est_quadrant_is_equal (&r, &s), "common_ancestor");
+    }
+  }
+
+  p = NULL;
+  for (iz = 0; iz < t2->quadrants.elem_count; ++iz) {
+    q1 = p4est_quadrant_array_index (&t2->quadrants, iz);
+
+    /* test the is_next function */
+    if (p != NULL) {
+      SC_CHECK_ABORT (p4est_quadrant_is_next (p, q1), "is_next");
+    }
+    p = q1;
+  }
+
+  /* test the coarsen function */
+  p4est_coarsen (p4est1, 1, coarsen_none, NULL);
+  p4est_coarsen (p4est1, 1, coarsen_all, NULL);
+  p4est_coarsen (p4est2, 1, coarsen_some, NULL);
+
+  /* test the linearize algorithm */
+  incount = t2->quadrants.elem_count;
+  (void) p4est_linearize_tree (p4est2, t2);
+  SC_CHECK_ABORT (incount == t2->quadrants.elem_count, "linearize");
+
+  /* this is user_data neutral only when p4est1->data_size == 0 */
+  sc_array_init (&tree.quadrants, sizeof (p4est_quadrant_t));
+  sc_array_resize (&tree.quadrants, 18);
+  q1 = p4est_quadrant_array_index (&tree.quadrants, 0);
+  q2 = p4est_quadrant_array_index (&t2->quadrants, 0);
+  *q1 = *q2;
+  q2 = p4est_quadrant_array_index (&t2->quadrants, 1);
+  for (k = 0; k < 3; ++k) {
+    q1 = p4est_quadrant_array_index (&tree.quadrants, (size_t) (k + 1));
+    *q1 = *q2;
+    q1->level = (int8_t) (q1->level + k);
+  }
+  for (k = 0; k < 10; ++k) {
+    q1 = p4est_quadrant_array_index (&tree.quadrants, (size_t) (k + 4));
+    q2 = p4est_quadrant_array_index (&t2->quadrants, (size_t) (k + 3));
+    *q1 = *q2;
+    q1->level = (int8_t) (q1->level + k);
+  }
+  for (k = 0; k < 4; ++k) {
+    q1 = p4est_quadrant_array_index (&tree.quadrants, (size_t) (k + 14));
+    q2 = p4est_quadrant_array_index (&t2->quadrants, (size_t) (k + 12));
+    *q1 = *q2;
+    q1->level = (int8_t) (q1->level + 10 + k);
+  }
+  tree.maxlevel = 0;
+  for (k = 0; k <= P4EST_QMAXLEVEL; ++k) {
+    tree.quadrants_per_level[k] = 0;
+  }
+  for (; k <= P4EST_MAXLEVEL; ++k) {
+    tree.quadrants_per_level[k] = -1;
+  }
+  incount = tree.quadrants.elem_count;
+  for (iz = 0; iz < incount; ++iz) {
+    q1 = p4est_quadrant_array_index (&tree.quadrants, iz);
+    ++tree.quadrants_per_level[q1->level];
+    tree.maxlevel = (int8_t) SC_MAX (tree.maxlevel, q1->level);
+  }
+  SC_CHECK_ABORT (!p4est_tree_is_linear (&tree), "is_linear");
+  (void) p4est_linearize_tree (p4est1, &tree);
+  SC_CHECK_ABORT (incount - 3 == tree.quadrants.elem_count, "linearize");
+  sc_array_reset (&tree.quadrants);
+
+  /* create a partial tree and check overlap */
+  sc_array_resize (&tree.quadrants, 4);
+  q1 = p4est_quadrant_array_index (&tree.quadrants, 0);
+  p4est_quadrant_set_morton (q1, 3, 191);
+  q1 = p4est_quadrant_array_index (&tree.quadrants, 1);
+  p4est_quadrant_set_morton (q1, 1, 3);
+  q1 = p4est_quadrant_array_index (&tree.quadrants, 2);
+  p4est_quadrant_set_morton (q1, 2, 32);
+  q1 = p4est_quadrant_array_index (&tree.quadrants, 3);
+  p4est_quadrant_set_morton (q1, 2, 33);
+  for (k = 0; k <= P4EST_QMAXLEVEL; ++k) {
+    tree.quadrants_per_level[k] = 0;
+  }
+  for (; k <= P4EST_MAXLEVEL; ++k) {
+    tree.quadrants_per_level[k] = -1;
+  }
+  tree.quadrants_per_level[1] = 1;
+  tree.quadrants_per_level[2] = 2;
+  tree.quadrants_per_level[3] = 1;
+  tree.maxlevel = 3;
+  p4est_quadrant_first_descendant (p4est_quadrant_array_index
+                                   (&tree.quadrants, 0), &tree.first_desc,
+                                   P4EST_QMAXLEVEL);
+  p4est_quadrant_last_descendant (p4est_quadrant_array_index
+                                  (&tree.quadrants,
+                                   tree.quadrants.elem_count - 1),
+                                  &tree.last_desc, P4EST_QMAXLEVEL);
+  SC_CHECK_ABORT (p4est_tree_is_complete (&tree), "is_complete");
+
+  p4est_quadrant_set_morton (&D, 0, 0);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &D), "overlaps 0");
+
+  p4est_quadrant_set_morton (&A, 1, 0);
+  SC_CHECK_ABORT (!p4est_quadrant_overlaps_tree (&tree, &A), "overlaps 1");
+  p4est_quadrant_set_morton (&A, 1, 2);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &A), "overlaps 2");
+  p4est_quadrant_set_morton (&A, 1, 3);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &A), "overlaps 3");
+  p4est_quadrant_set_morton (&A, 1, 4);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &A), "overlaps 4");
+  p4est_quadrant_set_morton (&A, 1, 5);
+  SC_CHECK_ABORT (!p4est_quadrant_overlaps_tree (&tree, &A), "overlaps 5");
+
+  p4est_quadrant_set_morton (&B, 3, 13);
+  SC_CHECK_ABORT (!p4est_quadrant_overlaps_tree (&tree, &B), "overlaps 6");
+  p4est_quadrant_set_morton (&B, 3, 191);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &B), "overlaps 7");
+  p4est_quadrant_set_morton (&B, 3, 271);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &B), "overlaps 8");
+  p4est_quadrant_set_morton (&B, 3, 272);
+  SC_CHECK_ABORT (!p4est_quadrant_overlaps_tree (&tree, &B), "overlaps 9");
+
+  p4est_quadrant_set_morton (&C, 4, 2175);
+  SC_CHECK_ABORT (p4est_quadrant_overlaps_tree (&tree, &C), "overlaps 10");
+  p4est_quadrant_set_morton (&C, 4, 2176);
+  SC_CHECK_ABORT (!p4est_quadrant_overlaps_tree (&tree, &C), "overlaps 11");
+
+  sc_array_reset (&tree.quadrants);
+
+  /* destroy the p4est and its connectivity structure */
+  p4est_destroy (p4est1);
+  p4est_destroy (p4est2);
+  p4est_connectivity_destroy (connectivity);
+
+  /* This will test the ability to address negative quadrants */
+  P4EST_QUADRANT_INIT (&A);
+  P4EST_QUADRANT_INIT (&B);
+  P4EST_QUADRANT_INIT (&C);
+  P4EST_QUADRANT_INIT (&D);
+  P4EST_QUADRANT_INIT (&E);
+  P4EST_QUADRANT_INIT (&F);
+  P4EST_QUADRANT_INIT (&G);
+  P4EST_QUADRANT_INIT (&H);
+  P4EST_QUADRANT_INIT (&I);
+  P4EST_QUADRANT_INIT (&P);
+  P4EST_QUADRANT_INIT (&Q);
+
+  A.x = -qone << P4EST_MAXLEVEL;
+  A.y = -qone << P4EST_MAXLEVEL;
+  A.z = 0;
+  A.level = 0;
+
+  B.x = qone << P4EST_MAXLEVEL;
+  B.y = -qone << P4EST_MAXLEVEL;
+  B.z = 0;
+  B.level = 0;
+
+  C.x = -qone << P4EST_MAXLEVEL;
+  C.y = qone << P4EST_MAXLEVEL;
+  C.z = 0;
+  C.level = 0;
+
+  D.x = qone << P4EST_MAXLEVEL;
+  D.y = qone << P4EST_MAXLEVEL;
+  D.z = 0;
+  D.level = 0;
+
+  /* this one is outside the 3x3 box */
+  E.x = -qone << (P4EST_MAXLEVEL + 1);
+  E.y = -qone;
+  E.z = -qone;
+  E.level = 0;
+
+  F.x = P4EST_ROOT_LEN + (P4EST_ROOT_LEN - mh);
+  F.y = P4EST_ROOT_LEN + (P4EST_ROOT_LEN - mh);
+  F.z = -qone << P4EST_MAXLEVEL;
+  F.level = P4EST_QMAXLEVEL;
+
+  G.x = -mh;
+  G.y = -mh;
+  G.z = -mh;
+  G.level = P4EST_QMAXLEVEL;
+
+  H.x = -qone << (P4EST_MAXLEVEL - 1);
+  H.y = -qone << (P4EST_MAXLEVEL - 1);
+  H.z = qone << (P4EST_MAXLEVEL - 1);
+  H.level = 1;
+
+  I.x = -qone << P4EST_MAXLEVEL;
+  I.y = -qone << (P4EST_MAXLEVEL - 1);
+  I.z = P4EST_ROOT_LEN + (P4EST_ROOT_LEN - mh);
+  I.level = P4EST_QMAXLEVEL;
+
+  P.x = -qone << P4EST_MAXLEVEL;
+  P.y = -qone << (P4EST_MAXLEVEL - 1);
+  P.z = qone << (P4EST_MAXLEVEL - 1);
+  P.level = 1;
+
+  Q.x = -2 * mh;
+  Q.y = -2 * mh;
+  Q.z = (qone << P4EST_MAXLEVEL) - 2 * mh;
+  Q.level = P4EST_QMAXLEVEL - 1;
+
+  SC_CHECK_ABORT (p4est_quadrant_compare (&B, &F) < 0, "Comp 1");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&A, &G) < 0, "Comp 2");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&F, &G) < 0, "Comp 3");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&A, &I) < 0, "Comp 4");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&D, &C) < 0, "Comp 5");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&B, &G) < 0, "Comp 6");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&G, &G) == 0, "Comp 7");
+
+  check_linear_id (&A, &A);
+  check_linear_id (&A, &B);
+  check_linear_id (&A, &C);
+  check_linear_id (&A, &D);
+  /* check_linear_id (&A, &E); */
+  check_linear_id (&A, &F);
+  check_linear_id (&A, &G);
+  check_linear_id (&A, &H);
+  check_linear_id (&A, &I);
+
+  check_linear_id (&B, &A);
+  check_linear_id (&B, &B);
+  check_linear_id (&B, &C);
+  check_linear_id (&B, &D);
+  /* check_linear_id (&B, &E); */
+  check_linear_id (&B, &F);
+  check_linear_id (&B, &G);
+  check_linear_id (&B, &H);
+  check_linear_id (&B, &I);
+
+  check_linear_id (&D, &A);
+  check_linear_id (&D, &B);
+  check_linear_id (&D, &C);
+  check_linear_id (&D, &D);
+  /* check_linear_id (&D, &E); */
+  check_linear_id (&D, &F);
+  check_linear_id (&D, &G);
+  check_linear_id (&D, &H);
+  check_linear_id (&D, &I);
+
+  check_linear_id (&G, &A);
+  check_linear_id (&G, &B);
+  check_linear_id (&G, &C);
+  check_linear_id (&G, &D);
+  /* check_linear_id (&G, &E); */
+  check_linear_id (&G, &F);
+  check_linear_id (&G, &G);
+  check_linear_id (&G, &H);
+  check_linear_id (&G, &I);
+
+  check_linear_id (&I, &A);
+  check_linear_id (&I, &B);
+  check_linear_id (&I, &C);
+  check_linear_id (&I, &D);
+  /* check_linear_id (&I, &E); */
+  check_linear_id (&I, &F);
+  check_linear_id (&I, &G);
+  check_linear_id (&I, &H);
+  check_linear_id (&I, &I);
+
+  check_linear_id (&P, &F);
+  check_linear_id (&P, &G);
+  check_linear_id (&P, &H);
+  check_linear_id (&P, &Q);
+
+  check_linear_id (&Q, &F);
+  check_linear_id (&Q, &B);
+  check_linear_id (&Q, &H);
+  check_linear_id (&Q, &I);
+
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&A) == 1, "is_extended A");
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&B) == 1, "is_extended B");
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&C) == 1, "is_extended C");
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&D) == 1, "is_extended D");
+  SC_CHECK_ABORT (!p4est_quadrant_is_extended (&E) == 1, "!is_extended E");
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&F) == 1, "is_extended F");
+  SC_CHECK_ABORT (p4est_quadrant_is_extended (&G) == 1, "is_extended G");
+
+  SC_CHECK_ABORT (p4est_quadrant_compare (&A, &A) == 0, "compare");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&A, &B) > 0, "compare");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&B, &A) < 0, "compare");
+
+  SC_CHECK_ABORT (p4est_quadrant_compare (&F, &F) == 0, "compare");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&G, &F) > 0, "compare");
+  SC_CHECK_ABORT (p4est_quadrant_compare (&F, &G) < 0, "compare");
+
+  A.p.piggy1.which_tree = 0;
+  B.p.piggy2.which_tree = 0;
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&A, &A) == 0,
+                  "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&A, &B) > 0, "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&B, &A) < 0, "compare_piggy");
+
+  F.p.which_tree = 0;
+  G.p.piggy1.which_tree = 0;
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&F, &F) == 0,
+                  "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&G, &F) > 0, "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&F, &G) < 0, "compare_piggy");
+
+  F.p.piggy2.which_tree = (p4est_topidx_t) P4EST_TOPIDX_MAX - 3;
+  G.p.which_tree = (p4est_topidx_t) P4EST_TOPIDX_MAX / 2;
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&F, &F) == 0,
+                  "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&G, &F) < 0, "compare_piggy");
+  SC_CHECK_ABORT (p4est_quadrant_compare_piggy (&F, &G) > 0, "compare_piggy");
+
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&A, &A) == 1, "is_equal");
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&F, &F) == 1, "is_equal");
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&G, &G) == 1, "is_equal");
+
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling (&P, &H) == 1, "is_sibling");
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling (&A, &H) == 0, "is_sibling");
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling_D (&P, &H) == 1, "is_sibling_D");
+  SC_CHECK_ABORT (p4est_quadrant_is_sibling_D (&A, &H) == 0, "is_sibling_D");
+
+  SC_CHECK_ABORT (p4est_quadrant_is_parent (&A, &H) == 1, "is_parent");
+  SC_CHECK_ABORT (p4est_quadrant_is_parent (&H, &A) == 0, "is_parent");
+  SC_CHECK_ABORT (p4est_quadrant_is_parent (&A, &Q) == 0, "is_parent");
+  SC_CHECK_ABORT (p4est_quadrant_is_parent_D (&A, &H) == 1, "is_parent_D");
+
+  SC_CHECK_ABORT (p4est_quadrant_is_ancestor (&A, &Q) == 1, "is_ancestor");
+  SC_CHECK_ABORT (p4est_quadrant_is_ancestor (&A, &A) == 0, "is_ancestor");
+
+  SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (&A, &P) == 1,
+                  "is_ancestor_D");
+  SC_CHECK_ABORT (p4est_quadrant_is_ancestor_D (&G, &G) == 0,
+                  "is_ancestor_D");
+
+  /* SC_CHECK_ABORT (p4est_quadrant_is_next (&F, &E) == 1, "is_next"); */
+  SC_CHECK_ABORT (p4est_quadrant_is_next (&A, &H) == 0, "is_next");
+  /* SC_CHECK_ABORT (p4est_quadrant_is_next_D (&F, &E) == 1, "is_next_D"); */
+  SC_CHECK_ABORT (p4est_quadrant_is_next_D (&A, &H) == 0, "is_next_D");
+
+  p4est_quadrant_parent (&H, &a);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&A, &a) == 1, "parent");
+
+  p4est_quadrant_sibling (&P, &h, 7);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&H, &h) == 1, "sibling");
+
+  p8est_quadrant_children (&A, &c0, &c1, &c2, &c3, &c4, &c5, &c6, &c7);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&c6, &P) == 1, "children");
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&c7, &H) == 1, "children");
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&c7, &Q) == 0, "children");
+
+  SC_CHECK_ABORT (p8est_quadrant_is_family (&c0, &c1, &c2, &c3,
+                                            &c4, &c5, &c6, &c7) == 1,
+                  "is_family");
+  id0 = p4est_quadrant_child_id (&c0);
+  id1 = p4est_quadrant_child_id (&c1);
+  id2 = p4est_quadrant_child_id (&c2);
+  id3 = p4est_quadrant_child_id (&c6);
+  SC_CHECK_ABORT (id0 == 0 && id1 == 1 && id2 == 2 && id3 == 6, "child_id");
+  SC_CHECK_ABORT (p4est_quadrant_child_id (&G) == 7, "child_id");
+
+  p4est_quadrant_first_descendant (&A, &c1, 1);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&c0, &c1) == 1,
+                  "first_descendant");
+
+  p4est_quadrant_last_descendant (&A, &g, P4EST_QMAXLEVEL - 1);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&Q, &g) == 1, "last_descendant");
+
+  Fid = p4est_quadrant_linear_id (&F, P4EST_QMAXLEVEL);
+  p4est_quadrant_set_morton (&f, P4EST_QMAXLEVEL, Fid);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&F, &f) == 1,
+                  "set_morton/linear_id");
+
+  Aid = p4est_quadrant_linear_id (&A, 0);
+  p4est_quadrant_set_morton (&a, 0, Aid);
+  SC_CHECK_ABORT (Aid == 27, "linear_id");
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&A, &a) == 1,
+                  "set_morton/linear_id");
+
+  p4est_nearest_common_ancestor (&P, &H, &a);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&A, &a) == 1, "ancestor");
+
+  p4est_nearest_common_ancestor_D (&P, &Q, &a);
+  SC_CHECK_ABORT (p4est_quadrant_is_equal (&A, &a) == 1, "ancestor_D");
+
+  for (k = 0; k < 27; ++k) {
+    p4est_quadrant_set_morton (&E, 0, (uint64_t) indices[k]);
+  }
+  p4est_quadrant_set_morton (&P, 0, 54);
+  p4est_quadrant_set_morton (&Q, 0, 55);
+  SC_CHECK_ABORT (p4est_quadrant_is_next (&P, &Q), "is_next");
+  SC_CHECK_ABORT (!p4est_quadrant_is_next (&A, &Q), "is_next");
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_reorder2.c b/test/test_reorder2.c
new file mode 100644
index 0000000..67ee46a
--- /dev/null
+++ b/test/test_reorder2.c
@@ -0,0 +1,69 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2011 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est.h>
+#else
+#include <p8est.h>
+#endif
+
+int
+main (int argc, char **argv)
+{
+  MPI_Comm            mpicomm;
+  int                 mpiret;
+  int                 size, rank;
+  p4est_connectivity_t *conn;
+  int                 k = 5;
+
+  mpiret = MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = MPI_COMM_WORLD;
+  mpiret = MPI_Comm_size (mpicomm, &size);
+  SC_CHECK_MPI (mpiret);
+  mpiret = MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+#ifndef P4_TO_P8
+  conn = p4est_connectivity_new_brick (16, 17, 0, 1);
+#else
+  conn = p8est_connectivity_new_brick (7, 8, 9, 0, 1, 1);
+#endif
+
+  p4est_connectivity_reorder (mpicomm, k, conn, P4EST_CONNECT_FULL);
+
+  SC_CHECK_ABORT (p4est_connectivity_is_valid (conn), "Bad reordering");
+
+  p4est_connectivity_destroy (conn);
+
+  /* clean up and exit */
+  sc_finalize ();
+
+  mpiret = MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_reorder3.c b/test/test_reorder3.c
new file mode 100644
index 0000000..9fa216a
--- /dev/null
+++ b/test/test_reorder3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2011 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_reorder2.c"
diff --git a/test/test_replace2.c b/test/test_replace2.c
new file mode 100644
index 0000000..08bec8b
--- /dev/null
+++ b/test/test_replace2.c
@@ -0,0 +1,157 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_algorithms.h>
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_communication.h>
+#include <p4est_vtk.h>
+#else
+#include <p8est_algorithms.h>
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_communication.h>
+#include <p8est_vtk.h>
+#endif
+
+#ifndef P4_TO_P8
+static int          refine_level = 5;
+#else
+static int          refine_level = 3;
+#endif
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  if (which_tree == 2 || which_tree == 3) {
+    return 0;
+  }
+
+  cid = p4est_quadrant_child_id (quadrant);
+
+  if (cid == P4EST_CHILDREN - 1 ||
+      (quadrant->x >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2) &&
+       quadrant->y >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#ifdef P4_TO_P8
+       && quadrant->z >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
+#endif
+      )) {
+    return 1;
+  }
+  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
+    return 0;
+  }
+  if (quadrant->level == 1 && cid == 2) {
+    return 1;
+  }
+  if (quadrant->x == P4EST_QUADRANT_LEN (2) &&
+      quadrant->y == P4EST_LAST_OFFSET (2)) {
+    return 1;
+  }
+  if (quadrant->y >= P4EST_QUADRANT_LEN (2)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int
+coarsen_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+            p4est_quadrant_t * q[])
+{
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (q), "Coarsen invocation");
+
+  return q[0]->y < P4EST_ROOT_LEN / 2;
+}
+
+static void
+replace_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+            int num_outgoing, p4est_quadrant_t * outgoing[],
+            int num_incoming, p4est_quadrant_t * incoming[])
+{
+  p4est_quadrant_t  **fam;
+  p4est_quadrant_t   *p;
+
+  P4EST_ASSERT (num_outgoing + num_incoming == P4EST_CHILDREN + 1);
+  P4EST_ASSERT (num_outgoing == 1 || num_incoming == 1);
+
+  if (num_outgoing == 1) {
+    p = outgoing[0];
+    fam = incoming;
+  }
+  else {
+    p = incoming[0];
+    fam = outgoing;
+  }
+
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (fam),
+                  P4EST_STRING "_replace_t family is not a family");
+  SC_CHECK_ABORT (p4est_quadrant_is_parent (p, fam[0]),
+                  P4EST_STRING
+                  "_replace_t incoming and outgoing don't align");
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpirank, mpisize;
+  int                 mpiret;
+  sc_MPI_Comm         mpicomm;
+  p4est_t            *p4est;
+  p4est_connectivity_t *connectivity;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* create connectivity and forest structures */
+#ifdef P4_TO_P8
+  connectivity = p8est_connectivity_new_rotcubes ();
+#else
+  connectivity = p4est_connectivity_new_star ();
+#endif
+  p4est = p4est_new_ext (mpicomm, connectivity, 15, 0, 0, 1, NULL, NULL);
+  p4est_refine_ext (p4est, 1, P4EST_QMAXLEVEL, refine_fn, NULL, replace_fn);
+  p4est_coarsen_ext (p4est, 1, 0, coarsen_fn, NULL, replace_fn);
+  p4est_balance_ext (p4est, P4EST_CONNECT_FULL, NULL, replace_fn);
+
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (connectivity);
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_replace3.c b/test/test_replace3.c
new file mode 100644
index 0000000..2ef3bb6
--- /dev/null
+++ b/test/test_replace3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_replace2.c"
diff --git a/test/test_search2.c b/test/test_search2.c
new file mode 100644
index 0000000..9bdebe9
--- /dev/null
+++ b/test/test_search2.c
@@ -0,0 +1,232 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_geometry.h>
+#include <p4est_search.h>
+#include <p4est_vtk.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_geometry.h>
+#include <p8est_search.h>
+#include <p8est_vtk.h>
+#endif
+
+typedef struct
+{
+  const char         *name;
+  p4est_quadrant_t    quad;
+}
+test_point_t;
+
+static const int    refine_level = 3;
+static int          found_count = -1;
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+
+  if ((int) quadrant->level >= refine_level)
+    return 0;
+
+  if (which_tree == 2 || which_tree == 5)
+    return 0;
+
+  cid = p4est_quadrant_child_id (quadrant);
+  if (cid == 0 || cid == 1 || cid == 6)
+    return 1;
+
+  if (quadrant->x >= P4EST_LAST_OFFSET (2)
+#ifdef P4_TO_P8
+      && quadrant->z >= P4EST_LAST_OFFSET (2)
+#endif
+    ) {
+    return 1;
+  }
+
+  return 0;
+}
+
+static int
+search_callback (p4est_t * p4est, p4est_topidx_t which_tree,
+                 p4est_quadrant_t * quadrant, p4est_locidx_t local_num,
+                 void *point)
+{
+  test_point_t       *p = (test_point_t *) point;
+  int                 is_leaf;
+  int                 is_match;
+
+  is_leaf = local_num >= 0;
+  P4EST_ASSERT (!is_leaf || local_num < p4est->local_num_quadrants);
+  P4EST_ASSERT (point != NULL);
+
+  P4EST_LDEBUGF ("Tree %lld quadrant %s level %d %d child %d leaf %d\n",
+                 (long long) which_tree, p->name,
+                 (int) p->quad.level, (int) quadrant->level,
+                 p4est_quadrant_child_id (quadrant), is_leaf);
+
+  if (which_tree != p->quad.p.piggy3.which_tree) {
+    return 0;
+  }
+
+  if (quadrant->level < p->quad.level) {
+    is_match = p4est_quadrant_is_ancestor (quadrant, &p->quad);
+    P4EST_LDEBUGF ("Ancestor for quadrant %s is %d\n", p->name, is_match);
+  }
+  else {
+    is_match = !p4est_quadrant_compare (quadrant, &p->quad);
+    P4EST_LDEBUGF ("Tree %lld same size quadrant %s match %d\n",
+                   (long long) which_tree, p->name, is_match);
+  }
+
+  if (is_match && is_leaf) {
+    p4est_locidx_t      num = -1;
+
+    if (quadrant->level < p->quad.level) {
+      num = p->quad.p.piggy3.local_num = -1;
+    }
+    else {
+      P4EST_ASSERT (local_num >= 0);
+      num = p->quad.p.piggy3.local_num = local_num;
+    }
+    P4EST_INFOF ("Matched quadrant %s at %lld\n", p->name, (long long) num);
+    p4est_quadrant_print (SC_LP_INFO, quadrant);
+    p4est_quadrant_print (SC_LP_INFO, &p->quad);
+    ++found_count;
+  }
+
+  return is_match;
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 found_total;
+  p4est_locidx_t      jt, Al, Bl;
+  p4est_connectivity_t *conn;
+  p4est_quadrant_t   *A, *B;
+  p4est_geometry_t   *geom;
+  p4est_t            *p4est;
+  sc_array_t         *points;
+  test_point_t       *p;
+  const char         *vtkname;
+
+  /* Initialize MPI */
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+
+  /* Initialize packages */
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  /* Create forest */
+#ifndef P4_TO_P8
+  conn = p4est_connectivity_new_star ();
+  geom = NULL;
+  vtkname = "test_search2";
+#else
+  conn = p8est_connectivity_new_sphere ();
+  geom = p8est_geometry_new_sphere (conn, 1., 0.191728, 0.039856);
+  vtkname = "test_search3";
+#endif
+  p4est = p4est_new_ext (mpicomm, conn, 0, 0, 0, 0, NULL, NULL);
+  p4est_refine (p4est, 1, refine_fn, NULL);
+  p4est_partition (p4est, 0, NULL);
+  p4est_vtk_write_file (p4est, geom, vtkname);
+
+  /* Prepare a point search -- fix size so the memory is not relocated */
+  points = sc_array_new_size (sizeof (test_point_t), 2);
+
+  /* A */
+  p = (test_point_t *) sc_array_index (points, 0);
+  p->name = "A";
+  A = &p->quad;
+  P4EST_QUADRANT_INIT (A);
+  p4est_quadrant_set_morton (A, 3, 23);
+  A->p.piggy3.which_tree = 0;
+  A->p.piggy3.local_num = -1;
+  Al = -1;
+
+  /* B */
+  p = (test_point_t *) sc_array_index (points, 1);
+  p->name = "B";
+  B = &p->quad;
+  P4EST_QUADRANT_INIT (B);
+  p4est_quadrant_set_morton (B, 2, 13);
+  B->p.piggy3.which_tree = conn->num_trees / 2;
+  B->p.piggy3.local_num = -1;
+  Bl = -1;
+
+  /* Find quadrant numbers if existing */
+  for (jt = p4est->first_local_tree; jt <= p4est->last_local_tree; ++jt) {
+    size_t              zz;
+    p4est_tree_t       *tree = p4est_tree_array_index (p4est->trees, jt);
+    p4est_quadrant_t   *quad;
+    sc_array_t         *tquadrants = &tree->quadrants;
+
+    for (zz = 0; zz < tquadrants->elem_count; ++zz) {
+      quad = p4est_quadrant_array_index (tquadrants, zz);
+      if (A->p.piggy3.which_tree == jt && !p4est_quadrant_compare (quad, A)) {
+        Al = tree->quadrants_offset + (p4est_locidx_t) zz;
+        P4EST_VERBOSEF ("Searching for A at %lld\n", (long long) Al);
+      }
+      if (B->p.piggy3.which_tree == jt && !p4est_quadrant_compare (quad, B)) {
+        Bl = tree->quadrants_offset + (p4est_locidx_t) zz;
+        P4EST_VERBOSEF ("Searching for B at %lld\n", (long long) Bl);
+      }
+    }
+  }
+
+  /* Go */
+  found_count = 0;
+  p4est_search (p4est, NULL, search_callback, points);
+  mpiret = sc_MPI_Allreduce (&found_count, &found_total,
+                             1, sc_MPI_INT, sc_MPI_SUM, mpicomm);
+  SC_CHECK_MPI (mpiret);
+  SC_CHECK_ABORT (found_total == (int) points->elem_count, "Point search");
+  SC_CHECK_ABORT (A->p.piggy3.local_num == Al, "Search A");
+  SC_CHECK_ABORT (B->p.piggy3.local_num == Bl, "Search B");
+
+  /* Clear memory */
+  sc_array_destroy (points);
+  p4est_destroy (p4est);
+  if (geom != NULL) {
+    p4est_geometry_destroy (geom);
+  }
+  p4est_connectivity_destroy (conn);
+
+  /* Finalize */
+  sc_finalize ();
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_search3.c b/test/test_search3.c
new file mode 100644
index 0000000..6b7a160
--- /dev/null
+++ b/test/test_search3.c
@@ -0,0 +1,25 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_search2.c"
diff --git a/test/test_valid2.c b/test/test_valid2.c
new file mode 100644
index 0000000..1e7a960
--- /dev/null
+++ b/test/test_valid2.c
@@ -0,0 +1,212 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_BACKWARD_DEALII
+#define P4EST_BACKWARD_DEALII
+#endif
+
+#ifndef P4_TO_P8
+#include <p4est_bits.h>
+#include <p4est_extended.h>
+#include <p4est_ghost.h>
+#include <p4est_nodes.h>
+#include <p4est_vtk.h>
+#else
+#include <p8est_bits.h>
+#include <p8est_extended.h>
+#include <p8est_ghost.h>
+#include <p8est_nodes.h>
+#include <p8est_vtk.h>
+#endif
+
+#ifndef P4_TO_P8
+static const int    refine_level = 5;
+#else
+static const int    refine_level = 4;
+#endif
+
+static p4est_balance_type_t
+check_backward_compatibility (void)
+{
+  p4est_balance_type_t b;
+
+  b = P4EST_CONNECT_FULL;
+  return b;
+}
+
+static int
+refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+           p4est_quadrant_t * quadrant)
+{
+  int                 cid;
+  int                 endlevel = refine_level + (int) (which_tree % 3);
+
+  if ((int) quadrant->level >= endlevel)
+    return 0;
+
+  cid = p4est_quadrant_child_id (quadrant);
+  if (cid == 0 || cid == 3
+#ifdef P4_TO_P8
+      || cid == 6
+#endif
+    ) {
+    return 1;
+  }
+
+  return 0;
+}
+
+static int
+coarsen_fn (p4est_t * p4est, p4est_topidx_t which_tree,
+            p4est_quadrant_t * q[])
+{
+  int                 pid;
+  p4est_quadrant_t    p;
+
+  SC_CHECK_ABORT (p4est_quadrant_is_familypv (q), "Coarsen invocation");
+
+  if (q[0]->level <= 2)
+    return 0;
+
+  p4est_quadrant_parent (q[0], &p);
+  pid = p4est_quadrant_child_id (&p);
+
+  return pid == 3;
+}
+
+static void
+check_all (sc_MPI_Comm mpicomm, p4est_connectivity_t * conn,
+           const char *vtkname, unsigned crc_expected, unsigned gcrc_expected)
+{
+  int                 mpiret;
+  unsigned            crc_computed, gcrc_computed;
+  long long           lsize[3], gsize[3];
+  size_t              size_conn, size_p4est, size_ghost;
+  p4est_t            *p4est;
+  p4est_nodes_t      *nodes;
+  p4est_ghost_t      *ghost;
+
+  P4EST_GLOBAL_STATISTICSF ("Testing configuration %s\n", vtkname);
+
+  p4est = p4est_new_ext (mpicomm, conn, 0, 0, 0, 0, NULL, NULL);
+  p4est_refine (p4est, 1, refine_fn, NULL);
+  p4est_coarsen (p4est, 1, coarsen_fn, NULL);
+  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
+  p4est_partition (p4est, 0, NULL);
+  p4est_vtk_write_file (p4est, NULL, vtkname);
+
+  crc_computed = p4est_checksum (p4est);
+  P4EST_GLOBAL_STATISTICSF ("Forest checksum 0x%08x\n", crc_computed);
+  if (p4est->mpisize == 2 && p4est->mpirank == 0) {
+    SC_CHECK_ABORT (crc_computed == crc_expected, "Forest checksum mismatch");
+  }
+
+  ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL);
+
+  /* compute total size of forest storage */
+  size_conn = p4est_connectivity_memory_used (conn);
+  size_p4est = p4est_memory_used (p4est);
+  size_ghost = p4est_ghost_memory_used (ghost);
+  lsize[0] = (long long) size_conn;
+  lsize[1] = (long long) size_p4est;
+  lsize[2] = (long long) size_ghost;
+  mpiret = sc_MPI_Reduce (lsize, gsize, 3, sc_MPI_LONG_LONG_INT, sc_MPI_SUM,
+                          0, mpicomm);
+  SC_CHECK_MPI (mpiret);
+  P4EST_GLOBAL_INFOF ("Global byte sizes: %lld %lld %lld\n",
+                      gsize[0], gsize[1], gsize[2]);
+
+  gcrc_computed = p4est_ghost_checksum (p4est, ghost);
+  P4EST_GLOBAL_STATISTICSF ("Ghost checksum 0x%08x\n", gcrc_computed);
+  if (p4est->mpisize == 2 && p4est->mpirank == 0) {
+    SC_CHECK_ABORT (gcrc_computed == gcrc_expected,
+                    "Ghost checksum mismatch");
+  }
+
+  nodes = p4est_nodes_new (p4est, ghost);
+  p4est_nodes_destroy (nodes);
+  p4est_ghost_destroy (ghost);
+
+  p4est_destroy (p4est);
+  p4est_connectivity_destroy (conn);
+}
+
+int
+main (int argc, char **argv)
+{
+  sc_MPI_Comm         mpicomm;
+  int                 mpiret;
+  int                 size, rank;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+  mpiret = sc_MPI_Comm_size (mpicomm, &size);
+  SC_CHECK_MPI (mpiret);
+  mpiret = sc_MPI_Comm_rank (mpicomm, &rank);
+  SC_CHECK_MPI (mpiret);
+
+  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
+  p4est_init (NULL, SC_LP_DEFAULT);
+
+  (void) check_backward_compatibility ();
+
+#ifndef P4_TO_P8
+  check_all (mpicomm, p4est_connectivity_new_unitsquare (),
+             "test_unitsquare", 0xef45243bU, 0xbc5d0907U);
+  check_all (mpicomm, p4est_connectivity_new_rotwrap (),
+             "test_rotwrap2", 0x266d2739U, 0x29a31248U);
+  check_all (mpicomm, p4est_connectivity_new_corner (),
+             "test_corner", 0x9dad92ccU, 0x937b27afU);
+  check_all (mpicomm, p4est_connectivity_new_moebius (),
+             "test_moebius", 0xbbc10f7fU, 0x09b6319eU);
+  check_all (mpicomm, p4est_connectivity_new_star (),
+             "test_star", 0xfb28233fU, 0x8e8a32b3);
+#else
+  check_all (mpicomm, p8est_connectivity_new_unitcube (),
+             "test_unitcube", 0x2574801fU, 0x312559a7U);
+  check_all (mpicomm, p8est_connectivity_new_periodic (),
+             "test_periodic3", 0xdc7e8a93U, 0x0787ca2dU);
+  check_all (mpicomm, p8est_connectivity_new_rotwrap (),
+             "test_rotwrap", 0xa675888dU, 0x626cbe90U);
+  check_all (mpicomm, p8est_connectivity_new_twocubes (),
+             "test_twocubes", 0x7188978aU, 0x4124bcabU);
+  check_all (mpicomm, p8est_connectivity_new_twowrap (),
+             "test_twowrap", 0x8e3f994cU, 0x9dd49e94);
+  check_all (mpicomm, p8est_connectivity_new_rotcubes (),
+             "test_rotcubes", 0xc0e1b235U, 0x974af07a);
+  check_all (mpicomm, p8est_connectivity_new_shell (),
+             "test_shell", 0x558723a2U, 0x4dedf35eU);
+  check_all (mpicomm,
+             p8est_connectivity_new_brick (2, 3, 4, 0, 0, 1),
+             "test_brick", 0x82174e14U, 0x211da6c5);
+#endif
+
+  /* clean up and exit */
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_valid3.c b/test/test_valid3.c
new file mode 100644
index 0000000..40290bb
--- /dev/null
+++ b/test/test_valid3.c
@@ -0,0 +1,29 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2010 The University of Texas System
+  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4EST_BACKWARD_DEALII
+#define P4EST_BACKWARD_DEALII
+#endif
+
+#include <p4est_to_p8est.h>
+#include "test_valid2.c"
diff --git a/test/test_wrap2.c b/test/test_wrap2.c
new file mode 100644
index 0000000..36e45d6
--- /dev/null
+++ b/test/test_wrap2.c
@@ -0,0 +1,129 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef P4_TO_P8
+#include <p4est_wrap.h>
+#else
+#include <p8est_wrap.h>
+#endif
+
+static int
+wrap_adapt_partition (p4est_wrap_t * wrap, int weight_exponent)
+{
+  if (p4est_wrap_adapt (wrap)) {
+    if (p4est_wrap_partition (wrap, weight_exponent)) {
+      p4est_wrap_complete (wrap);
+    }
+    return 1;
+  }
+
+  return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+  int                 mpiret;
+  int                 changed;
+  int                 loop;
+#ifdef P4EST_ENABLE_DEBUG
+  int                 lp = SC_LP_DEFAULT;
+#else
+  int                 lp = SC_LP_PRODUCTION;
+#endif
+  p4est_locidx_t      jl;
+  p4est_wrap_leaf_t  *leaf;
+  p4est_ghost_t      *ghost;
+  p4est_mesh_t       *mesh;
+  sc_MPI_Comm         mpicomm;
+  p4est_wrap_t       *wrap;
+
+  mpiret = sc_MPI_Init (&argc, &argv);
+  SC_CHECK_MPI (mpiret);
+  mpicomm = sc_MPI_COMM_WORLD;
+
+  sc_init (mpicomm, 0, 0, NULL, lp);
+  p4est_init (NULL, lp);
+
+#ifndef P4_TO_P8
+  wrap = p4est_wrap_new_rotwrap (mpicomm, 0);
+#else
+  wrap = p8est_wrap_new_rotwrap (mpicomm, 0);
+#endif
+  ghost = p4est_wrap_get_ghost (wrap);
+  SC_CHECK_ABORT (ghost != NULL, "Get ghost");
+  ghost = NULL;
+  mesh = p4est_wrap_get_mesh (wrap);
+  SC_CHECK_ABORT (mesh != NULL, "Get mesh");
+  mesh = NULL;
+
+  for (loop = 0; loop < 3; ++loop) {
+    /* mark for refinement */
+    for (jl = 0, leaf = p4est_wrap_leaf_first (wrap); leaf != NULL;
+         jl++, leaf = p4est_wrap_leaf_next (leaf)) {
+      if (leaf->which_quad % 3 == 0) {
+        p4est_wrap_mark_refine (wrap, leaf->which_tree, leaf->which_quad);
+      }
+    }
+    SC_CHECK_ABORT (jl == wrap->p4est->local_num_quadrants, "Iterator");
+
+    changed = wrap_adapt_partition (wrap, 1);
+    SC_CHECK_ABORT (changed, "Wrap refine");
+  }
+
+  for (loop = 0; loop < 2; ++loop) {
+    /* mark some elements for coarsening that does not effect anything */
+    for (jl = 0, leaf = p4est_wrap_leaf_first (wrap); leaf != NULL;
+         jl++, leaf = p4est_wrap_leaf_next (leaf)) {
+      if (leaf->which_quad % 5 == 0) {
+        p4est_wrap_mark_refine (wrap, leaf->which_tree, leaf->which_quad);
+        p4est_wrap_mark_coarsen (wrap, leaf->which_tree, leaf->which_quad);
+      }
+    }
+    SC_CHECK_ABORT (jl == wrap->p4est->local_num_quadrants, "Iterator");
+
+    changed = wrap_adapt_partition (wrap, 0);
+    SC_CHECK_ABORT (!changed, "Wrap noop");
+  }
+  
+  for (loop = 0; loop < 2; ++loop) {
+    /* mark for coarsening */
+    for (jl = 0, leaf = p4est_wrap_leaf_first (wrap); leaf != NULL;
+         jl++, leaf = p4est_wrap_leaf_next (leaf)) {
+      if ((leaf->which_quad / 13) % 17 != 3) {
+        p4est_wrap_mark_coarsen (wrap, leaf->which_tree, leaf->which_quad);
+      }
+    }
+    SC_CHECK_ABORT (jl == wrap->p4est->local_num_quadrants, "Iterator");
+
+    (void) wrap_adapt_partition (wrap, 0);
+  }
+
+  p4est_wrap_destroy (wrap);
+
+  sc_finalize ();
+
+  mpiret = sc_MPI_Finalize ();
+  SC_CHECK_MPI (mpiret);
+
+  return 0;
+}
diff --git a/test/test_wrap3.c b/test/test_wrap3.c
new file mode 100644
index 0000000..c2e1816
--- /dev/null
+++ b/test/test_wrap3.c
@@ -0,0 +1,24 @@
+/*
+  This file is part of p4est.
+  p4est is a C library to manage a collection (a forest) of multiple
+  connected adaptive quadtrees or octrees in parallel.
+
+  Copyright (C) 2012 Carsten Burstedde
+
+  p4est is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  p4est 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 p4est; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <p4est_to_p8est.h>
+#include "test_wrap2.c"

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/p4est.git



More information about the debian-science-commits mailing list