[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, ¢er)) {
+ return 1;
+ }
+ if (p4est_quadrant_is_equal (quadrant, ¢er)) {
+ ((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, ¢er, balance, NULL)) {
+ break;
+ }
+ }
+ if (i == P4EST_CHILDREN) {
+ P4EST_ASSERT (!p4est_balance_seeds (quadrant, ¢er, 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, ¢er, 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,
+ ©_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, ®ion) - 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